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.
|