diff --git a/src/share/classes/sun/security/krb5/Config.java b/src/share/classes/sun/security/krb5/Config.java index 84f80c6dd89a3476430666ddf7a4132a04a14077..eb2f335c551785cc89250331e16c643117a7d875 100644 --- a/src/share/classes/sun/security/krb5/Config.java +++ b/src/share/classes/sun/security/krb5/Config.java @@ -30,22 +30,20 @@ */ package sun.security.krb5; -import java.io.File; -import java.io.FileInputStream; -import java.util.Hashtable; -import java.util.Vector; -import java.util.ArrayList; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.io.IOException; -import java.util.StringTokenizer; +import java.io.*; import java.net.InetAddress; import java.net.UnknownHostException; import java.security.AccessController; import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Hashtable; import java.util.List; import java.util.Locale; +import java.util.StringTokenizer; +import java.util.Vector; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import sun.net.dns.ResolverConfiguration; import sun.security.krb5.internal.crypto.EType; import sun.security.krb5.internal.Krb5; @@ -349,6 +347,72 @@ public class Config { } } + /** + * Translates a duration value into seconds. + * + * The format can be one of "h:m[:s]", "NdNhNmNs", and "N". See + * http://web.mit.edu/kerberos/krb5-devel/doc/basic/date_format.html#duration + * for definitions. + * + * @param s the string duration + * @return time in seconds + * @throw KrbException if format is illegal + */ + public static int duration(String s) throws KrbException { + + if (s.isEmpty()) { + throw new KrbException("Duration cannot be empty"); + } + + // N + if (s.matches("\\d+")) { + return Integer.parseInt(s); + } + + // h:m[:s] + Matcher m = Pattern.compile("(\\d+):(\\d+)(:(\\d+))?").matcher(s); + if (m.matches()) { + int hr = Integer.parseInt(m.group(1)); + int min = Integer.parseInt(m.group(2)); + if (min >= 60) { + throw new KrbException("Illegal duration format " + s); + } + int result = hr * 3600 + min * 60; + if (m.group(4) != null) { + int sec = Integer.parseInt(m.group(4)); + if (sec >= 60) { + throw new KrbException("Illegal duration format " + s); + } + result += sec; + } + return result; + } + + // NdNhNmNs + // 120m allowed. Maybe 1h120m is not good, but still allowed + m = Pattern.compile( + "((\\d+)d)?\\s*((\\d+)h)?\\s*((\\d+)m)?\\s*((\\d+)s)?", + Pattern.CASE_INSENSITIVE).matcher(s); + if (m.matches()) { + int result = 0; + if (m.group(2) != null) { + result += 86400 * Integer.parseInt(m.group(2)); + } + if (m.group(4) != null) { + result += 3600 * Integer.parseInt(m.group(4)); + } + if (m.group(6) != null) { + result += 60 * Integer.parseInt(m.group(6)); + } + if (m.group(8) != null) { + result += Integer.parseInt(m.group(8)); + } + return result; + } + + throw new KrbException("Illegal duration format " + s); + } + /** * Gets the int value for the specified keys. * @param keys the keys diff --git a/src/share/classes/sun/security/krb5/Credentials.java b/src/share/classes/sun/security/krb5/Credentials.java index 3fe29530717811791e727cfe86aee0c8204862fd..195b86663343a8a0082772fe688962536d704633 100644 --- a/src/share/classes/sun/security/krb5/Credentials.java +++ b/src/share/classes/sun/security/krb5/Credentials.java @@ -531,4 +531,23 @@ public class Credentials { return buffer.toString(); } + public sun.security.krb5.internal.ccache.Credentials toCCacheCreds() { + return new sun.security.krb5.internal.ccache.Credentials( + getClient(), getServer(), + getSessionKey(), + date2kt(getAuthTime()), + date2kt(getStartTime()), + date2kt(getEndTime()), + date2kt(getRenewTill()), + false, + flags, + new HostAddresses(getClientAddresses()), + getAuthzData(), + getTicket(), + null); + } + + private static KerberosTime date2kt(Date d) { + return d == null ? null : new KerberosTime(d); + } } diff --git a/src/share/classes/sun/security/krb5/KrbAsReq.java b/src/share/classes/sun/security/krb5/KrbAsReq.java index 55930ff870e51b9cebec70cb1949fac36629a682..19e3ef61076237894fdb8e6e46791d8c363d1e3a 100644 --- a/src/share/classes/sun/security/krb5/KrbAsReq.java +++ b/src/share/classes/sun/security/krb5/KrbAsReq.java @@ -35,6 +35,7 @@ import sun.security.krb5.internal.*; import sun.security.krb5.internal.crypto.Nonce; import sun.security.krb5.internal.crypto.KeyUsage; import java.io.IOException; +import java.time.Instant; import java.util.Arrays; /** @@ -66,7 +67,6 @@ public class KrbAsReq { if (options == null) { options = new KDCOptions(); } - // check if they are valid arguments. The optional fields should be // consistent with settings in KDCOptions. Mar 17 2000 if (options.get(KDCOptions.FORWARDED) || @@ -84,12 +84,6 @@ public class KrbAsReq { } else { if (from != null) from = null; } - if (options.get(KDCOptions.RENEWABLE)) { - // if (rtime == null) - // throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); - } else { - if (rtime != null) rtime = null; - } PAData[] paData = null; if (pakey != null) { @@ -120,8 +114,10 @@ public class KrbAsReq { System.out.println(">>> KrbAsReq creating message"); } + Config cfg = Config.getInstance(); + // check to use addresses in tickets - if (addresses == null && Config.getInstance().useAddresses()) { + if (addresses == null && cfg.useAddresses()) { addresses = HostAddresses.getLocalAddresses(); } @@ -131,7 +127,26 @@ public class KrbAsReq { } if (till == null) { - till = new KerberosTime(0); // Choose KDC maximum allowed + String d = cfg.get("libdefaults", "ticket_lifetime"); + if (d != null) { + till = new KerberosTime(Instant.now().plusSeconds(Config.duration(d))); + } else { + till = new KerberosTime(0); // Choose KDC maximum allowed + } + } + + if (rtime == null) { + String d = cfg.get("libdefaults", "renew_lifetime"); + if (d != null) { + rtime = new KerberosTime(Instant.now().plusSeconds(Config.duration(d))); + } + } + + if (rtime != null) { + options.set(KDCOptions.RENEWABLE, true); + if (till.greaterThan(rtime)) { + rtime = till; + } } // enc-authorization-data and additional-tickets never in AS-REQ diff --git a/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java b/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java index 3ca7b6d71ac8a09d167aef71c9eb6125c11305e3..58e5c9418747d436b367e481e39b19c0d6bc065a 100644 --- a/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java +++ b/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java @@ -224,6 +224,16 @@ public final class KrbAsReqBuilder { this.options = options; } + public void setTill(KerberosTime till) { + checkState(State.INIT, "Cannot specify till"); + this.till = till; + } + + public void setRTime(KerberosTime rtime) { + checkState(State.INIT, "Cannot specify rtime"); + this.rtime = rtime; + } + /** * Sets or clears target. If cleared, KrbAsReq might choose krbtgt * for cname realm diff --git a/src/share/classes/sun/security/krb5/KrbKdcRep.java b/src/share/classes/sun/security/krb5/KrbKdcRep.java index 7dc21cd21c5b8e799e028f71d1d18913a73e282c..da4a8fa400321e0ab1814effcdb78efed7db8409 100644 --- a/src/share/classes/sun/security/krb5/KrbKdcRep.java +++ b/src/share/classes/sun/security/krb5/KrbKdcRep.java @@ -100,24 +100,27 @@ abstract class KrbKdcRep { !rep.encKDCRepPart.flags.get(KDCOptions.RENEWABLE)) { throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); } - if ((req.reqBody.from == null) || req.reqBody.from.isZero()) + + if ((req.reqBody.from == null) || req.reqBody.from.isZero()) { // verify this is allowed if ((rep.encKDCRepPart.starttime != null) && - !rep.encKDCRepPart.starttime.inClockSkew()) { + !rep.encKDCRepPart.starttime.inClockSkew()) { rep.encKDCRepPart.key.destroy(); throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW); } + } - if ((req.reqBody.from != null) && !req.reqBody.from.isZero()) + if ((req.reqBody.from != null) && !req.reqBody.from.isZero()) { // verify this is allowed if ((rep.encKDCRepPart.starttime != null) && - !req.reqBody.from.equals(rep.encKDCRepPart.starttime)) { + !req.reqBody.from.equals(rep.encKDCRepPart.starttime)) { rep.encKDCRepPart.key.destroy(); throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); } + } if (!req.reqBody.till.isZero() && - rep.encKDCRepPart.endtime.greaterThan(req.reqBody.till)) { + rep.encKDCRepPart.endtime.greaterThan(req.reqBody.till)) { rep.encKDCRepPart.key.destroy(); throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); } @@ -164,27 +167,17 @@ abstract class KrbKdcRep { } } - if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE)) - if (req.reqBody.rtime != null && !req.reqBody.rtime.isZero()) - // verify this is required + if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE)) { + if (req.reqBody.rtime != null && !req.reqBody.rtime.isZero()) { + // verify this is required if ((rep.encKDCRepPart.renewTill == null) || - rep.encKDCRepPart.renewTill.greaterThan(req.reqBody.rtime) - ) { + rep.encKDCRepPart.renewTill.greaterThan(req.reqBody.rtime) + ) { rep.encKDCRepPart.key.destroy(); throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); } - if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE_OK) && - rep.encKDCRepPart.flags.get(KDCOptions.RENEWABLE)) - if (!req.reqBody.till.isZero()) - // verify this is required - if ((rep.encKDCRepPart.renewTill == null) || - rep.encKDCRepPart.renewTill.greaterThan(req.reqBody.till) - ) { - rep.encKDCRepPart.key.destroy(); - throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); - } + } + } } - - } diff --git a/src/share/classes/sun/security/krb5/internal/HostAddresses.java b/src/share/classes/sun/security/krb5/internal/HostAddresses.java index 8dc9a2853e16229f445823c81503b1e15e307750..7f18b98f9c3d20fde76b1e75cfde5a5634977ce6 100644 --- a/src/share/classes/sun/security/krb5/internal/HostAddresses.java +++ b/src/share/classes/sun/security/krb5/internal/HostAddresses.java @@ -250,6 +250,10 @@ public class HostAddresses implements Cloneable { */ public void writeAddrs(CCacheOutputStream cos) throws IOException { + if (addresses == null || addresses.length == 0) { + cos.write32(0); + return; + } cos.write32(addresses.length); for (int i = 0; i < addresses.length; i++) { cos.write16(addresses[i].addrType); diff --git a/src/share/classes/sun/security/krb5/internal/KerberosTime.java b/src/share/classes/sun/security/krb5/internal/KerberosTime.java index 3beaac873ced90df80925fa6dc7128f3e6e0dbbe..64225c8eede8e0064b417f88f1e51c35238d6f1a 100644 --- a/src/share/classes/sun/security/krb5/internal/KerberosTime.java +++ b/src/share/classes/sun/security/krb5/internal/KerberosTime.java @@ -38,6 +38,7 @@ import sun.security.util.DerOutputStream; import sun.security.util.DerValue; import java.io.IOException; +import java.time.Instant; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; @@ -128,6 +129,14 @@ public class KerberosTime { this(time.getTime(), 0); } + /** + * Creates a KerberosTime object from an Instant object + */ + public KerberosTime(Instant instant) { + this(instant.getEpochSecond()*1000 + instant.getNano()/1000000L, + instant.getNano()/1000%1000); + } + /** * Creates a KerberosTime object for now. It uses System.nanoTime() * to get a more precise time than "new Date()". diff --git a/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java b/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java index a436d585cba1939d6fff03d4b4ee2c99cff4abdc..5839a5e13e4a72488b3f0abe482736b8a7e84ec7 100644 --- a/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java +++ b/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java @@ -36,7 +36,6 @@ import sun.security.krb5.internal.*; import sun.security.krb5.internal.ccache.*; import java.io.IOException; import java.util.Arrays; -import javax.security.auth.kerberos.KerberosPrincipal; import sun.security.util.Password; import javax.security.auth.kerberos.KeyTab; @@ -53,22 +52,9 @@ public class Kinit { /** * The main method is used to accept user command line input for ticket - * request. - *
- * Usage: kinit [-A] [-f] [-p] [-c cachename] [[-k [-t keytab_file_name]] - * [principal] [password] - *
- * Use java sun.security.krb5.tools.Kinit -help to bring up help menu. + * request. Read {@link KinitOptions#printHelp} for usages or call + * java sun.security.krb5.internal.tools.Kinit -help + * to bring up help menu. *
* We currently support only file-based credentials cache to
* store the tickets obtained from the KDC.
@@ -146,6 +132,49 @@ public class Kinit {
} 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);
+ }
+ }
+
+ private void renew()
+ throws IOException, RealmException, KrbException {
+
+ PrincipalName principal = options.getPrincipal();
+ String realm = principal.getRealmAsString();
+ CredentialsCache cache = CredentialsCache.getInstance(options.cachename);
+
+ if (cache == null) {
+ throw new IOException("Unable to find existing cache file " +
+ options.cachename);
+ }
+ sun.security.krb5.internal.ccache.Credentials credentials =
+ cache.getCreds(PrincipalName.tgsService(realm, realm));
+
+ credentials = credentials.setKrbCreds()
+ .renew()
+ .toCCacheCreds();
+
+ cache = CredentialsCache.create(principal, options.cachename);
+ if (cache == null) {
+ throw new IOException("Unable to create the cache file " +
+ options.cachename);
+ }
+ cache.update(credentials);
+ cache.save();
+ }
+
+ private void acquire()
+ throws IOException, RealmException, KrbException {
+
String princName = null;
PrincipalName principal = options.getPrincipal();
if (principal != null) {
@@ -216,6 +245,9 @@ public class Kinit {
if (options.getAddressOption())
builder.setAddresses(HostAddresses.getLocalAddresses());
+ builder.setTill(options.lifetime);
+ builder.setRTime(options.renewable_lifetime);
+
builder.action();
sun.security.krb5.internal.ccache.Credentials credentials =
diff --git a/src/windows/classes/sun/security/krb5/internal/tools/KinitOptions.java b/src/windows/classes/sun/security/krb5/internal/tools/KinitOptions.java
index e7507a0a27fd31695f1a04b4656f80ccb31a545e..23bfecad83748c617db441d6d9d9d7f040ae733d 100644
--- a/src/windows/classes/sun/security/krb5/internal/tools/KinitOptions.java
+++ b/src/windows/classes/sun/security/krb5/internal/tools/KinitOptions.java
@@ -33,12 +33,8 @@ package sun.security.krb5.internal.tools;
import sun.security.krb5.*;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.ccache.*;
-import java.io.File;
import java.io.IOException;
-import java.util.StringTokenizer;
-import java.util.Vector;
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
+import java.time.Instant;
import java.io.FileInputStream;
/**
@@ -49,14 +45,15 @@ import java.io.FileInputStream;
* @author Ram Marti
*/
class KinitOptions {
- public boolean validate = false;
+
+ // 1. acquire, 2. renew, 3. validate
+ public int action = 1;
// forwardable and proxiable flags have two states:
// -1 - flag set to be not forwardable or proxiable;
// 1 - flag set to be forwardable or proxiable.
- public short forwardable = -1;
- public short proxiable = -1;
- public boolean renew = false;
+ public short forwardable = 0;
+ public short proxiable = 0;
public KerberosTime lifetime;
public KerberosTime renewable_lifetime;
public String target_service;
@@ -134,6 +131,12 @@ class KinitOptions {
}
useKeytab = true;
+ } else if (args[i].equals("-R")) {
+ action = 2;
+ } else if (args[i].equals("-l")) {
+ lifetime = getTime(Config.duration(args[++i]));
+ } else if (args[i].equals("-r")) {
+ renewable_lifetime = getTime(Config.duration(args[++i]));
} else if (args[i].equalsIgnoreCase("-help")) {
printHelp();
System.exit(0);
@@ -223,23 +226,28 @@ class KinitOptions {
void printHelp() {
- System.out.println("Usage: kinit " +
- "[-A] [-f] [-p] [-c cachename] " +
- "[[-k [-t keytab_file_name]] [principal] " +
+ System.out.println("Usage:\n\n1. Initial ticket request:\n" +
+ " kinit [-A] [-f] [-p] [-c cachename] " +
+ "[-l lifetime] [-r renewable_time]\n" +
+ " [[-k [-t keytab_file_name]] [principal] " +
"[password]");
- System.out.println("\tavailable options to " +
+ System.out.println("2. Renew a ticket:\n" +
+ " kinit -R [-c cachename] [principal]");
+ System.out.println("\nAvailable options to " +
"Kerberos 5 ticket request:");
- System.out.println("\t -A do not include addresses");
- System.out.println("\t -f forwardable");
- System.out.println("\t -p proxiable");
- System.out.println("\t -c cache name " +
- "(i.e., FILE:\\d:\\myProfiles\\mykrb5cache)");
- System.out.println("\t -k use keytab");
- System.out.println("\t -t keytab file name");
- System.out.println("\t principal the principal name "+
- "(i.e., qweadf@ATHENA.MIT.EDU qweadf)");
- System.out.println("\t password " +
- "the principal's Kerberos password");
+ System.out.println("\t-A do not include addresses");
+ System.out.println("\t-f forwardable");
+ System.out.println("\t-p proxiable");
+ System.out.println("\t-c cache name " +
+ "(i.e., FILE:\\d:\\myProfiles\\mykrb5cache)");
+ System.out.println("\t-l lifetime");
+ System.out.println("\t-r renewable time " +
+ "(total lifetime a ticket can be renewed)");
+ System.out.println("\t-k use keytab");
+ System.out.println("\t-t keytab file name");
+ System.out.println("\tprincipal the principal name "+
+ "(i.e., qweadf@ATHENA.MIT.EDU qweadf)");
+ System.out.println("\tpassword the principal's Kerberos password");
}
public boolean getAddressOption() {
@@ -257,4 +265,8 @@ class KinitOptions {
public PrincipalName getPrincipal() {
return principal;
}
+
+ private KerberosTime getTime(int s) {
+ return new KerberosTime(Instant.now().plusSeconds(s));
+ }
}
diff --git a/test/sun/security/krb5/auto/KDC.java b/test/sun/security/krb5/auto/KDC.java
index f81582d5aeb843d0f41a841c5beb8185b76409b9..0a7cf3f69c0ec6ba2550b1cb00c8cfcbfc43b556 100644
--- a/test/sun/security/krb5/auto/KDC.java
+++ b/test/sun/security/krb5/auto/KDC.java
@@ -40,6 +40,7 @@ import sun.net.spi.nameservice.NameServiceDescriptor;
import sun.security.krb5.*;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.ccache.CredentialsCache;
+import sun.security.krb5.internal.crypto.EType;
import sun.security.krb5.internal.crypto.KeyUsage;
import sun.security.krb5.internal.ktab.KeyTab;
import sun.security.util.DerInputStream;
@@ -127,11 +128,11 @@ import java.util.regex.Pattern;
*/
public class KDC {
- // Under the hood.
-
public static final int DEFAULT_LIFETIME = 39600;
public static final int DEFAULT_RENEWTIME = 86400;
+ // Under the hood.
+
// The random generator to generate random keys (including session keys)
private static SecureRandom secureRandom = new SecureRandom();
@@ -242,7 +243,8 @@ public class KDC {
* A standalone KDC server.
*/
public static void main(String[] args) throws Exception {
- KDC kdc = create("RABBIT.HOLE", "kdc.rabbit.hole", 0, false);
+ int port = args.length > 0 ? Integer.parseInt(args[0]) : 0;
+ KDC kdc = create("RABBIT.HOLE", "kdc.rabbit.hole", port, false);
kdc.addPrincipal("dummy", "bogus".toCharArray());
kdc.addPrincipal("foo", "bar".toCharArray());
kdc.addPrincipalRandKey("krbtgt/RABBIT.HOLE");
@@ -830,10 +832,16 @@ public class KDC {
// Check time, TODO
KerberosTime till = body.till;
+ KerberosTime rtime = body.rtime;
if (till == null) {
throw new KrbException(Krb5.KDC_ERR_NEVER_VALID); // TODO
} else if (till.isZero()) {
- till = new KerberosTime(new Date().getTime() + 1000 * 3600 * 11);
+ till = new KerberosTime(
+ new Date().getTime() + 1000 * DEFAULT_LIFETIME);
+ }
+ if (rtime == null && body.kdcOptions.get(KDCOptions.RENEWABLE)) {
+ rtime = new KerberosTime(
+ new Date().getTime() + 1000 * DEFAULT_RENEWTIME);
}
boolean[] bFlags = new boolean[Krb5.TKT_OPTS_MAX+1];
@@ -939,7 +947,7 @@ public class KDC {
tFlags,
new KerberosTime(new Date()),
body.from,
- till, body.rtime,
+ till, rtime,
service,
body.addresses != null // always set caddr
? body.addresses
@@ -1008,6 +1016,14 @@ public class KDC {
eTypes = KDCReqBodyDotEType(body);
int eType = eTypes[0];
+ // Maybe server does not support aes256, but a kinit does
+ if (!EType.isSupported(eType)) {
+ if (eTypes.length < 2) {
+ throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
+ }
+ eType = eTypes[1];
+ }
+
if (body.kdcOptions.get(KDCOptions.CANONICALIZE) &&
body.cname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) {
PrincipalName principal = alias2Principals.get(
diff --git a/test/sun/security/krb5/auto/LifeTimeInSeconds.java b/test/sun/security/krb5/auto/LifeTimeInSeconds.java
index 9d4c6d7b017f44607e6de1794c62820e95773660..23c953183ba3c07671ed6354df593155484a95c8 100644
--- a/test/sun/security/krb5/auto/LifeTimeInSeconds.java
+++ b/test/sun/security/krb5/auto/LifeTimeInSeconds.java
@@ -40,7 +40,7 @@ public class LifeTimeInSeconds {
int time = cred.getRemainingLifetime();
int time2 = cred.getRemainingInitLifetime(null);
// The test KDC issues a TGT with a default lifetime of 11 hours
- int elevenhrs = 11*3600;
+ int elevenhrs = KDC.DEFAULT_LIFETIME;
if (time > elevenhrs+60 || time < elevenhrs-60) {
throw new Exception("getRemainingLifetime returns wrong value.");
}
diff --git a/test/sun/security/krb5/auto/Renewal.java b/test/sun/security/krb5/auto/Renewal.java
new file mode 100644
index 0000000000000000000000000000000000000000..38639c761052dc241d2cc6e60af9ac860bc0fba0
--- /dev/null
+++ b/test/sun/security/krb5/auto/Renewal.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2012, 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 8044500
+ * @summary Add kinit options and krb5.conf flags that allow users to
+ * obtain renewable tickets and specify ticket lifetimes
+ * @library ../../../../java/security/testlibrary/
+ * @compile -XDignore.symbol.file Renewal.java
+ * @run main/othervm -Dsun.net.spi.nameservice.provider.1=ns,mock Renewal
+ */
+
+import sun.security.jgss.GSSUtil;
+import sun.security.krb5.Config;
+import sun.security.krb5.internal.ccache.Credentials;
+import sun.security.krb5.internal.ccache.FileCredentialsCache;
+
+import javax.security.auth.kerberos.KerberosTicket;
+import java.util.Date;
+import java.util.Random;
+import java.util.Set;
+
+// The basic krb5 test skeleton you can copy from
+public class Renewal {
+
+ static OneKDC kdc;
+ static String clazz = "sun.security.krb5.internal.tools.Kinit";
+
+ public static void main(String[] args) throws Exception {
+
+ kdc = new OneKDC(null);
+ kdc.writeJAASConf();
+ kdc.setOption(KDC.Option.PREAUTH_REQUIRED, false);
+
+ checkLogin(null, null, KDC.DEFAULT_LIFETIME, -1);
+ checkLogin("1h", null, 3600, -1);
+ checkLogin(null, "2d", KDC.DEFAULT_LIFETIME, 86400*2);
+ checkLogin("1h", "10h", 3600, 36000);
+ // When rtime is before till, use till as rtime
+ checkLogin("10h", "1h", 36000, 36000);
+
+ try {
+ Class.forName(clazz);
+ } catch (ClassNotFoundException cnfe) {
+ return;
+ }
+
+ checkKinit(null, null, null, null, KDC.DEFAULT_LIFETIME, -1);
+ checkKinit("1h", "10h", null, null, 3600, 36000);
+ checkKinit(null, null, "30m", "5h", 1800, 18000);
+ checkKinit("1h", "10h", "30m", "5h", 1800, 18000);
+
+ checkKinitRenew();
+ }
+
+ static int count = 0;
+
+ static void checkKinit(
+ String s1, // ticket_lifetime in krb5.conf, null if none
+ String s2, // renew_lifetime in krb5.conf, null if none
+ String c1, // -l on kinit, null if none
+ String c2, // -r on kinit, null if none
+ int t1, int t2 // expected lifetimes, -1 of unexpected
+ ) throws Exception {
+ KDC.saveConfig(OneKDC.KRB5_CONF, kdc,
+ s1 != null ? ("ticket_lifetime = " + s1) : "",
+ s2 != null ? ("renew_lifetime = " + s2) : "");
+ Proc p = Proc.create(clazz);
+ if (c1 != null) {
+ p.args("-l", c1);
+ }
+ if (c2 != null) {
+ p.args("-r", c2);
+ }
+ count++;
+ p.args(OneKDC.USER, new String(OneKDC.PASS))
+ .inheritIO()
+ .prop("sun.net.spi.nameservice.provider.1", "ns,mock")
+ .prop("java.security.krb5.conf", OneKDC.KRB5_CONF)
+ .env("KRB5CCNAME", "ccache" + count)
+ .start();
+ if (p.waitFor() != 0) {
+ throw new Exception();
+ }
+ FileCredentialsCache fcc =
+ FileCredentialsCache.acquireInstance(null, "ccache" + count);
+ Credentials cred = fcc.getDefaultCreds();
+ checkRough(cred.getEndTime().toDate(), t1);
+ if (cred.getRenewTill() == null) {
+ checkRough(null, t2);
+ } else {
+ checkRough(cred.getRenewTill().toDate(), t2);
+ }
+ }
+
+ static void checkKinitRenew() throws Exception {
+ Proc p = Proc.create(clazz)
+ .args("-R")
+ .inheritIO()
+ .prop("sun.net.spi.nameservice.provider.1", "ns,mock")
+ .prop("java.security.krb5.conf", OneKDC.KRB5_CONF)
+ .env("KRB5CCNAME", "ccache" + count)
+ .start();
+ if (p.waitFor() != 0) {
+ throw new Exception();
+ }
+ }
+
+ static void checkLogin(
+ String s1, // ticket_lifetime in krb5.conf, null if none
+ String s2, // renew_lifetime in krb5.conf, null if none
+ int t1, int t2 // expected lifetimes, -1 of unexpected
+ ) throws Exception {
+ KDC.saveConfig(OneKDC.KRB5_CONF, kdc,
+ s1 != null ? ("ticket_lifetime = " + s1) : "",
+ s2 != null ? ("renew_lifetime = " + s2) : "");
+ Config.refresh();
+
+ Context c;
+ c = Context.fromJAAS("client");
+
+ Set