< prev index next >

test/sun/security/krb5/auto/KDC.java

Print this page


   1 /*
   2  * Copyright (c) 2008, 2017, 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 import java.lang.reflect.Constructor;
  25 import java.lang.reflect.Field;
  26 import java.lang.reflect.InvocationTargetException;
  27 import java.net.*;
  28 import java.io.*;
  29 import java.lang.reflect.Method;
  30 import java.security.SecureRandom;




  31 import java.util.*;
  32 import java.util.concurrent.*;
  33 
  34 import sun.net.spi.nameservice.NameService;
  35 import sun.net.spi.nameservice.NameServiceDescriptor;
  36 import sun.security.krb5.*;
  37 import sun.security.krb5.internal.*;
  38 import sun.security.krb5.internal.ccache.CredentialsCache;
  39 import sun.security.krb5.internal.crypto.KeyUsage;
  40 import sun.security.krb5.internal.ktab.KeyTab;
  41 import sun.security.util.DerInputStream;
  42 import sun.security.util.DerOutputStream;
  43 import sun.security.util.DerValue;


  44 
  45 /**
  46  * A KDC server.
  47  * <p>
  48  * Features:
  49  * <ol>
  50  * <li> Supports TCP and UDP
  51  * <li> Supports AS-REQ and TGS-REQ
  52  * <li> Principal db and other settings hard coded in application
  53  * <li> Options, say, request preauth or not
  54  * </ol>
  55  * Side effects:
  56  * <ol>
  57  * <li> The Sun-internal class <code>sun.security.krb5.Config</code> is a
  58  * singleton and initialized according to Kerberos settings (krb5.conf and
  59  * java.security.krb5.* system properties). This means once it's initialized
  60  * it will not automatically notice any changes to these settings (or file
  61  * changes of krb5.conf). The KDC class normally does not touch these
  62  * settings (except for the <code>writeKtab()</code> method). However, to make
  63  * sure nothing ever goes wrong, if you want to make any changes to these


 106  * Note that you need to add the principal name krbtgt/REALM.NAME yourself.
 107  * <p>
 108  * Note that you can safely add a principal at any time after the KDC is
 109  * started and before a user requests info on this principal.
 110  * <p>
 111  * 3. Other public methods:
 112  * <ul>
 113  * <li> <code>getPort</code>: Returns the port number the KDC uses
 114  * <li> <code>getRealm</code>: Returns the realm name
 115  * <li> <code>writeKtab</code>: Writes all principals' keys into a keytab file
 116  * <li> <code>saveConfig</code>: Saves a krb5.conf file to access this KDC
 117  * <li> <code>setOption</code>: Sets various options
 118  * </ul>
 119  * Read the javadoc for details. Lazy developer can use <code>OneKDC</code>
 120  * directly.
 121  */
 122 public class KDC {
 123 
 124     // Under the hood.
 125 



 126     // The random generator to generate random keys (including session keys)
 127     private static SecureRandom secureRandom = new SecureRandom();
 128 
 129     // Principal db. principal -> pass. A case-insensitive TreeMap is used
 130     // so that even if the client provides a name with different case, the KDC
 131     // can still locate the principal and give back correct salt.
 132     private TreeMap<String,char[]> passwords = new TreeMap<>
 133             (String.CASE_INSENSITIVE_ORDER);
 134 
 135     // Realm name
 136     private String realm;
 137     // KDC
 138     private String kdc;
 139     // Service port number
 140     private int port;
 141     // The request/response job queue
 142     private BlockingQueue<Job> q = new ArrayBlockingQueue<>(100);
 143     // Options
 144     private Map<Option,Object> options = new HashMap<>();
 145     // Realm-specific krb5.conf settings


 603             Integer kvno = null;
 604             // For service whose password ending with a number, use it as kvno.
 605             // Kvno must be postive.
 606             if (p.toString().indexOf('/') > 0) {
 607                 char[] pass = getPassword(p, server);
 608                 if (Character.isDigit(pass[pass.length-1])) {
 609                     kvno = pass[pass.length-1] - '0';
 610                 }
 611             }
 612             return new EncryptionKey(EncryptionKeyDotStringToKey(
 613                     getPassword(p, server), getSalt(p), null, etype),
 614                     etype, kvno);
 615         } catch (KrbException ke) {
 616             throw ke;
 617         } catch (Exception e) {
 618             throw new RuntimeException(e);  // should not happen
 619         }
 620     }
 621 
 622     /**









 623      * Processes an incoming request and generates a response.
 624      * @param in the request
 625      * @return the response
 626      * @throws java.lang.Exception for various errors
 627      */
 628     protected byte[] processMessage(byte[] in) throws Exception {
 629         if ((in[0] & 0x1f) == Krb5.KRB_AS_REQ)
 630             return processAsReq(in);
 631         else
 632             return processTgsReq(in);
 633     }
 634 
 635     /**
 636      * Processes a TGS_REQ and generates a TGS_REP (or KRB_ERROR)
 637      * @param in the request
 638      * @return the response
 639      * @throws java.lang.Exception for various errors
 640      */
 641     protected byte[] processTgsReq(byte[] in) throws Exception {
 642         TGSReq tgsReq = new TGSReq(in);


 902                         found = true;
 903                         break;
 904                     }
 905                 }
 906                 if (!found) {
 907                     throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
 908                 }
 909                 skey = keyForUser(service, tgtEType, true);
 910             }
 911             if (ckey == null) {
 912                 throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
 913             }
 914             if (skey == null) {
 915                 throw new KrbException(Krb5.KDC_ERR_SUMTYPE_NOSUPP); // TODO
 916             }
 917 
 918             // Session key
 919             EncryptionKey key = generateRandomKey(eType);
 920             // Check time, TODO
 921             KerberosTime till = body.till;

 922             if (till == null) {
 923                 throw new KrbException(Krb5.KDC_ERR_NEVER_VALID); // TODO
 924             } else if (till.isZero()) {
 925                 till = new KerberosTime(new Date().getTime() + 1000 * 3600 * 11);











 926             }






 927             //body.from
 928             boolean[] bFlags = new boolean[Krb5.TKT_OPTS_MAX+1];
 929             if (body.kdcOptions.get(KDCOptions.FORWARDABLE)) {
 930                 List<String> sensitives = (List<String>)
 931                         options.get(Option.SENSITIVE_ACCOUNTS);
 932                 if (sensitives != null && sensitives.contains(body.cname.toString())) {
 933                     // Cannot make FORWARDABLE
 934                 } else {
 935                     bFlags[Krb5.TKT_OPTS_FORWARDABLE] = true;
 936                 }
 937             }
 938             if (body.kdcOptions.get(KDCOptions.RENEWABLE)) {
 939                 bFlags[Krb5.TKT_OPTS_RENEWABLE] = true;
 940                 //renew = new KerberosTime(new Date().getTime() + 1000 * 3600 * 24 * 7);
 941             }
 942             if (body.kdcOptions.get(KDCOptions.PROXIABLE)) {
 943                 bFlags[Krb5.TKT_OPTS_PROXIABLE] = true;
 944             }
 945             if (body.kdcOptions.get(KDCOptions.POSTDATED)) {
 946                 bFlags[Krb5.TKT_OPTS_POSTDATED] = true;


1062                 }
1063             } else {
1064                 try {
1065                     EncryptedData data = newEncryptedData(new DerValue(inPAs[0].getValue()));
1066                     EncryptionKey pakey = keyForUser(body.cname, data.getEType(), false);
1067                     data.decrypt(pakey, KeyUsage.KU_PA_ENC_TS);
1068                 } catch (Exception e) {
1069                     throw new KrbException(Krb5.KDC_ERR_PREAUTH_FAILED);
1070                 }
1071                 bFlags[Krb5.TKT_OPTS_PRE_AUTHENT] = true;
1072             }
1073 
1074             TicketFlags tFlags = new TicketFlags(bFlags);
1075             EncTicketPart enc = new EncTicketPart(
1076                     tFlags,
1077                     key,
1078                     body.cname,
1079                     new TransitedEncoding(1, new byte[0]),
1080                     new KerberosTime(new Date()),
1081                     body.from,
1082                     till, body.rtime,
1083                     body.addresses,
1084                     null);
1085             Ticket t = new Ticket(
1086                     service,
1087                     new EncryptedData(skey, enc.asn1Encode(), KeyUsage.KU_TICKET)
1088             );
1089             EncASRepPart enc_part = new EncASRepPart(
1090                     key,
1091                     new LastReq(new LastReqEntry[]{
1092                         new LastReqEntry(0, new KerberosTime(new Date().getTime() - 10000))
1093                     }),
1094                     body.getNonce(),    // TODO: detect replay?
1095                     new KerberosTime(new Date().getTime() + 1000 * 3600 * 24),
1096                     // Next 5 and last MUST be same with ticket
1097                     tFlags,
1098                     new KerberosTime(new Date()),
1099                     body.from,
1100                     till, body.rtime,
1101                     service,
1102                     body.addresses
1103                     );
1104             EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(), KeyUsage.KU_ENC_AS_REP_PART);
1105             ASRep asRep = new ASRep(
1106                     outPAs.toArray(new PAData[outPAs.size()]),
1107                     body.cname,
1108                     t,
1109                     edata);
1110 
1111             System.out.println("     Return " + asRep.cname
1112                     + " ticket for " + asRep.ticket.sname + ", flags "
1113                     + tFlags);
1114 
1115             DerOutputStream out = new DerOutputStream();
1116             out.write(DerValue.createTag(DerValue.TAG_APPLICATION,
1117                     true, (byte)Krb5.KRB_AS_REP), asRep.asn1Encode());
1118             byte[] result = out.toByteArray();
1119 
1120             // Added feature:


1149                     DerOutputStream bytes = new DerOutputStream();
1150                     bytes.write(new PAData(Krb5.PA_ENC_TIMESTAMP, new byte[0]).asn1Encode());
1151                     for (PAData p: outPAs) {
1152                         bytes.write(p.asn1Encode());
1153                     }
1154                     DerOutputStream temp = new DerOutputStream();
1155                     temp.write(DerValue.tag_Sequence, bytes);
1156                     eData = temp.toByteArray();
1157                 }
1158                 kerr = new KRBError(null, null, null,
1159                         new KerberosTime(new Date()),
1160                         0,
1161                         ke.returnCode(),
1162                         body.cname,
1163                         service,
1164                         KrbException.errorMessage(ke.returnCode()),
1165                         eData);
1166             }
1167             return kerr.asn1Encode();
1168         }


































































1169     }
1170 
1171     /**
1172      * Generates a line for a KDC to put inside [realms] of krb5.conf
1173      * @return REALM.NAME = { kdc = host:port etc }
1174      */
1175     private String realmLine() {
1176         StringBuilder sb = new StringBuilder();
1177         sb.append(realm).append(" = {\n    kdc = ")
1178                 .append(kdc).append(':').append(port).append('\n');
1179         for (String s: conf) {
1180             sb.append("    ").append(s).append('\n');
1181         }
1182         return sb.append("}\n").toString();
1183     }
1184 
1185     /**
1186      * Start the KDC service. This server listens on both UDP and TCP using
1187      * the same port number. It uses three threads to deal with requests.
1188      * They can be set to daemon threads if requested.


   1 /*
   2  * Copyright (c) 2008, 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 import java.lang.reflect.Constructor;
  25 import java.lang.reflect.Field;
  26 import java.lang.reflect.InvocationTargetException;
  27 import java.net.*;
  28 import java.io.*;
  29 import java.lang.reflect.Method;
  30 import java.security.SecureRandom;
  31 import java.time.Instant;
  32 import java.time.temporal.ChronoUnit;
  33 import java.time.temporal.TemporalAmount;
  34 import java.time.temporal.TemporalUnit;
  35 import java.util.*;
  36 import java.util.concurrent.*;
  37 
  38 import sun.net.spi.nameservice.NameService;
  39 import sun.net.spi.nameservice.NameServiceDescriptor;
  40 import sun.security.krb5.*;
  41 import sun.security.krb5.internal.*;
  42 import sun.security.krb5.internal.ccache.CredentialsCache;
  43 import sun.security.krb5.internal.crypto.KeyUsage;
  44 import sun.security.krb5.internal.ktab.KeyTab;
  45 import sun.security.util.DerInputStream;
  46 import sun.security.util.DerOutputStream;
  47 import sun.security.util.DerValue;
  48 import java.util.regex.Matcher;
  49 import java.util.regex.Pattern;
  50 
  51 /**
  52  * A KDC server.
  53  * <p>
  54  * Features:
  55  * <ol>
  56  * <li> Supports TCP and UDP
  57  * <li> Supports AS-REQ and TGS-REQ
  58  * <li> Principal db and other settings hard coded in application
  59  * <li> Options, say, request preauth or not
  60  * </ol>
  61  * Side effects:
  62  * <ol>
  63  * <li> The Sun-internal class <code>sun.security.krb5.Config</code> is a
  64  * singleton and initialized according to Kerberos settings (krb5.conf and
  65  * java.security.krb5.* system properties). This means once it's initialized
  66  * it will not automatically notice any changes to these settings (or file
  67  * changes of krb5.conf). The KDC class normally does not touch these
  68  * settings (except for the <code>writeKtab()</code> method). However, to make
  69  * sure nothing ever goes wrong, if you want to make any changes to these


 112  * Note that you need to add the principal name krbtgt/REALM.NAME yourself.
 113  * <p>
 114  * Note that you can safely add a principal at any time after the KDC is
 115  * started and before a user requests info on this principal.
 116  * <p>
 117  * 3. Other public methods:
 118  * <ul>
 119  * <li> <code>getPort</code>: Returns the port number the KDC uses
 120  * <li> <code>getRealm</code>: Returns the realm name
 121  * <li> <code>writeKtab</code>: Writes all principals' keys into a keytab file
 122  * <li> <code>saveConfig</code>: Saves a krb5.conf file to access this KDC
 123  * <li> <code>setOption</code>: Sets various options
 124  * </ul>
 125  * Read the javadoc for details. Lazy developer can use <code>OneKDC</code>
 126  * directly.
 127  */
 128 public class KDC {
 129 
 130     // Under the hood.
 131 
 132     public static final int DEFAULT_LIFETIME = 39600;
 133     public static final int DEFAULT_RENEWTIME = 86400;
 134 
 135     // The random generator to generate random keys (including session keys)
 136     private static SecureRandom secureRandom = new SecureRandom();
 137 
 138     // Principal db. principal -> pass. A case-insensitive TreeMap is used
 139     // so that even if the client provides a name with different case, the KDC
 140     // can still locate the principal and give back correct salt.
 141     private TreeMap<String,char[]> passwords = new TreeMap<>
 142             (String.CASE_INSENSITIVE_ORDER);
 143 
 144     // Realm name
 145     private String realm;
 146     // KDC
 147     private String kdc;
 148     // Service port number
 149     private int port;
 150     // The request/response job queue
 151     private BlockingQueue<Job> q = new ArrayBlockingQueue<>(100);
 152     // Options
 153     private Map<Option,Object> options = new HashMap<>();
 154     // Realm-specific krb5.conf settings


 612             Integer kvno = null;
 613             // For service whose password ending with a number, use it as kvno.
 614             // Kvno must be postive.
 615             if (p.toString().indexOf('/') > 0) {
 616                 char[] pass = getPassword(p, server);
 617                 if (Character.isDigit(pass[pass.length-1])) {
 618                     kvno = pass[pass.length-1] - '0';
 619                 }
 620             }
 621             return new EncryptionKey(EncryptionKeyDotStringToKey(
 622                     getPassword(p, server), getSalt(p), null, etype),
 623                     etype, kvno);
 624         } catch (KrbException ke) {
 625             throw ke;
 626         } catch (Exception e) {
 627             throw new RuntimeException(e);  // should not happen
 628         }
 629     }
 630 
 631     /**
 632      * Returns a KerberosTime.
 633      *
 634      * @param offset offset from NOW in seconds
 635      */
 636     private static KerberosTime timeAfter(int offset) {
 637         return new KerberosTime(new Date().getTime() + offset * 1000L);
 638     }
 639 
 640     /**
 641      * Processes an incoming request and generates a response.
 642      * @param in the request
 643      * @return the response
 644      * @throws java.lang.Exception for various errors
 645      */
 646     protected byte[] processMessage(byte[] in) throws Exception {
 647         if ((in[0] & 0x1f) == Krb5.KRB_AS_REQ)
 648             return processAsReq(in);
 649         else
 650             return processTgsReq(in);
 651     }
 652 
 653     /**
 654      * Processes a TGS_REQ and generates a TGS_REP (or KRB_ERROR)
 655      * @param in the request
 656      * @return the response
 657      * @throws java.lang.Exception for various errors
 658      */
 659     protected byte[] processTgsReq(byte[] in) throws Exception {
 660         TGSReq tgsReq = new TGSReq(in);


 920                         found = true;
 921                         break;
 922                     }
 923                 }
 924                 if (!found) {
 925                     throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
 926                 }
 927                 skey = keyForUser(service, tgtEType, true);
 928             }
 929             if (ckey == null) {
 930                 throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
 931             }
 932             if (skey == null) {
 933                 throw new KrbException(Krb5.KDC_ERR_SUMTYPE_NOSUPP); // TODO
 934             }
 935 
 936             // Session key
 937             EncryptionKey key = generateRandomKey(eType);
 938             // Check time, TODO
 939             KerberosTime till = body.till;
 940             KerberosTime rtime = body.rtime;
 941             if (till == null) {
 942                 throw new KrbException(Krb5.KDC_ERR_NEVER_VALID); // TODO
 943             } else if (till.isZero()) {
 944                 String ttlsVal = System.getProperty("test.kdc.ttl.value");                   
 945                 if (ttlsVal != null){
 946                    till = timeAfter(duration(ttlsVal));                   
 947                    if(till.greaterThan(timeAfter(24 * 3600)) &&
 948                      (System.getProperty("test.kdc.force.till") == null)) {
 949                       till = timeAfter(DEFAULT_LIFETIME);
 950                       body.kdcOptions.set(KDCOptions.RENEWABLE, true);                      
 951                    }
 952                    
 953                 }
 954                 else{
 955                    till = timeAfter(DEFAULT_LIFETIME);          
 956                 }
 957             }
 958 
 959             if (rtime == null && body.kdcOptions.get(KDCOptions.RENEWABLE)) {
 960                 rtime = timeAfter(DEFAULT_RENEWTIME);
 961             }
 962 
 963             //body.from
 964             boolean[] bFlags = new boolean[Krb5.TKT_OPTS_MAX+1];
 965             if (body.kdcOptions.get(KDCOptions.FORWARDABLE)) {
 966                 List<String> sensitives = (List<String>)
 967                         options.get(Option.SENSITIVE_ACCOUNTS);
 968                 if (sensitives != null && sensitives.contains(body.cname.toString())) {
 969                     // Cannot make FORWARDABLE
 970                 } else {
 971                     bFlags[Krb5.TKT_OPTS_FORWARDABLE] = true;
 972                 }
 973             }
 974             if (body.kdcOptions.get(KDCOptions.RENEWABLE)) {
 975                 bFlags[Krb5.TKT_OPTS_RENEWABLE] = true;
 976                 //renew = new KerberosTime(new Date().getTime() + 1000 * 3600 * 24 * 7);
 977             }
 978             if (body.kdcOptions.get(KDCOptions.PROXIABLE)) {
 979                 bFlags[Krb5.TKT_OPTS_PROXIABLE] = true;
 980             }
 981             if (body.kdcOptions.get(KDCOptions.POSTDATED)) {
 982                 bFlags[Krb5.TKT_OPTS_POSTDATED] = true;


1098                 }
1099             } else {
1100                 try {
1101                     EncryptedData data = newEncryptedData(new DerValue(inPAs[0].getValue()));
1102                     EncryptionKey pakey = keyForUser(body.cname, data.getEType(), false);
1103                     data.decrypt(pakey, KeyUsage.KU_PA_ENC_TS);
1104                 } catch (Exception e) {
1105                     throw new KrbException(Krb5.KDC_ERR_PREAUTH_FAILED);
1106                 }
1107                 bFlags[Krb5.TKT_OPTS_PRE_AUTHENT] = true;
1108             }
1109 
1110             TicketFlags tFlags = new TicketFlags(bFlags);
1111             EncTicketPart enc = new EncTicketPart(
1112                     tFlags,
1113                     key,
1114                     body.cname,
1115                     new TransitedEncoding(1, new byte[0]),
1116                     new KerberosTime(new Date()),
1117                     body.from,
1118                     till, rtime,
1119                     body.addresses,
1120                     null);
1121             Ticket t = new Ticket(
1122                     service,
1123                     new EncryptedData(skey, enc.asn1Encode(), KeyUsage.KU_TICKET)
1124             );
1125             EncASRepPart enc_part = new EncASRepPart(
1126                     key,
1127                     new LastReq(new LastReqEntry[]{
1128                         new LastReqEntry(0, new KerberosTime(new Date().getTime() - 10000))
1129                     }),
1130                     body.getNonce(),    // TODO: detect replay?
1131                     new KerberosTime(new Date().getTime() + 1000 * 3600 * 24),
1132                     // Next 5 and last MUST be same with ticket
1133                     tFlags,
1134                     new KerberosTime(new Date()),
1135                     body.from,
1136                     till, rtime,
1137                     service,
1138                     body.addresses
1139                     );
1140             EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(), KeyUsage.KU_ENC_AS_REP_PART);
1141             ASRep asRep = new ASRep(
1142                     outPAs.toArray(new PAData[outPAs.size()]),
1143                     body.cname,
1144                     t,
1145                     edata);
1146 
1147             System.out.println("     Return " + asRep.cname
1148                     + " ticket for " + asRep.ticket.sname + ", flags "
1149                     + tFlags);
1150 
1151             DerOutputStream out = new DerOutputStream();
1152             out.write(DerValue.createTag(DerValue.TAG_APPLICATION,
1153                     true, (byte)Krb5.KRB_AS_REP), asRep.asn1Encode());
1154             byte[] result = out.toByteArray();
1155 
1156             // Added feature:


1185                     DerOutputStream bytes = new DerOutputStream();
1186                     bytes.write(new PAData(Krb5.PA_ENC_TIMESTAMP, new byte[0]).asn1Encode());
1187                     for (PAData p: outPAs) {
1188                         bytes.write(p.asn1Encode());
1189                     }
1190                     DerOutputStream temp = new DerOutputStream();
1191                     temp.write(DerValue.tag_Sequence, bytes);
1192                     eData = temp.toByteArray();
1193                 }
1194                 kerr = new KRBError(null, null, null,
1195                         new KerberosTime(new Date()),
1196                         0,
1197                         ke.returnCode(),
1198                         body.cname,
1199                         service,
1200                         KrbException.errorMessage(ke.returnCode()),
1201                         eData);
1202             }
1203             return kerr.asn1Encode();
1204         }
1205     }
1206 
1207     /**
1208      * Translates a duration value into seconds.
1209      *
1210      * The format can be one of "h:m[:s]", "NdNhNmNs", and "N". See
1211      * http://web.mit.edu/kerberos/krb5-devel/doc/basic/date_format.html#duration
1212      * for definitions.
1213      *
1214      * @param s the string duration
1215      * @return time in seconds
1216      * @throw KrbException if format is illegal
1217      */
1218     public static int duration(String s) throws KrbException {
1219 
1220         if (s.isEmpty()) {
1221             throw new KrbException("Duration cannot be empty");
1222         }
1223 
1224         // N
1225         if (s.matches("\\d+")) {
1226             return Integer.parseInt(s);
1227         }
1228 
1229         // h:m[:s]
1230         Matcher m = Pattern.compile("(\\d+):(\\d+)(:(\\d+))?").matcher(s);
1231         if (m.matches()) {
1232             int hr = Integer.parseInt(m.group(1));
1233             int min = Integer.parseInt(m.group(2));
1234             if (min >= 60) {
1235                 throw new KrbException("Illegal duration format " + s);
1236             }
1237             int result = hr * 3600 + min * 60;
1238             if (m.group(4) != null) {
1239                 int sec = Integer.parseInt(m.group(4));
1240                 if (sec >= 60) {
1241                     throw new KrbException("Illegal duration format " + s);
1242                 }
1243                 result += sec;
1244             }
1245             return result;
1246         }
1247 
1248         // NdNhNmNs
1249         // 120m allowed. Maybe 1h120m is not good, but still allowed
1250         m = Pattern.compile(
1251                     "((\\d+)d)?\\s*((\\d+)h)?\\s*((\\d+)m)?\\s*((\\d+)s)?",
1252                 Pattern.CASE_INSENSITIVE).matcher(s);
1253         if (m.matches()) {
1254             int result = 0;
1255             if (m.group(2) != null) {
1256                 result += 86400 * Integer.parseInt(m.group(2));
1257             }
1258             if (m.group(4) != null) {
1259                 result += 3600 * Integer.parseInt(m.group(4));
1260             }
1261             if (m.group(6) != null) {
1262                 result += 60 * Integer.parseInt(m.group(6));
1263             }
1264             if (m.group(8) != null) {
1265                 result += Integer.parseInt(m.group(8));
1266             }
1267             return result;
1268         }
1269 
1270         throw new KrbException("Illegal duration format " + s);
1271     }
1272 
1273     /**
1274      * Generates a line for a KDC to put inside [realms] of krb5.conf
1275      * @return REALM.NAME = { kdc = host:port etc }
1276      */
1277     private String realmLine() {
1278         StringBuilder sb = new StringBuilder();
1279         sb.append(realm).append(" = {\n    kdc = ")
1280                 .append(kdc).append(':').append(port).append('\n');
1281         for (String s: conf) {
1282             sb.append("    ").append(s).append('\n');
1283         }
1284         return sb.append("}\n").toString();
1285     }
1286 
1287     /**
1288      * Start the KDC service. This server listens on both UDP and TCP using
1289      * the same port number. It uses three threads to deal with requests.
1290      * They can be set to daemon threads if requested.


< prev index next >