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 tickets = + c.s().getPrivateCredentials(KerberosTicket.class); + if (tickets.size() != 1) { + throw new Exception(); + } + KerberosTicket ticket = tickets.iterator().next(); + + checkRough(ticket.getEndTime(), t1); + checkRough(ticket.getRenewTill(), t2); + } + + static void checkRough(Date t, int duration) throws Exception { + Date now = new Date(); + if (t == null && duration == -1) { + return; + } + long change = (t.getTime() - System.currentTimeMillis()) / 1000; + if (change > duration + 20 || change < duration - 20) { + throw new Exception(t + " is not " + duration); + } + } +} diff --git a/test/sun/security/krb5/config/Duration.java b/test/sun/security/krb5/config/Duration.java new file mode 100644 index 0000000000000000000000000000000000000000..d9c0f3961c780a6eeff0e269ce9f3fc1b983b32b --- /dev/null +++ b/test/sun/security/krb5/config/Duration.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014, 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 + * @compile -XDignore.symbol.file Duration.java + * @run main Duration + */ +import sun.security.krb5.Config; +import sun.security.krb5.KrbException; + +public class Duration { + public static void main(String[] args) throws Exception { + check("123", 123); + check("1:1", 3660); + check("1:1:1", 3661); + check("1d", 86400); + check("1h", 3600); + check("1h1m", 3660); + check("1h 1m", 3660); + check("1d 1h 1m 1s", 90061); + check("1d1h1m1s", 90061); + + check("", -1); + check("abc", -1); + check("1ms", -1); + check("1d1d", -1); + check("1h1d", -1); + check("x1h", -1); + check("1h x 1m", -1); + check(":", -1); + check("1:60", -1); + check("1:1:1:1", -1); + check("1:1:1:", -1); + } + + static void check(String s, int ex) throws Exception { + System.out.print("\u001b[1;37;41m" +s + " " + ex); + System.out.print("\u001b[m\n"); + try { + int result = Config.duration(s); + if (result != ex) throw new Exception("for " + s + " is " + result); + } catch (KrbException ke) { + ke.printStackTrace(); + if (ex != -1) throw new Exception(); + } + } +}