148
149 // Under the hood.
150
151 // Principal db. principal -> pass. A case-insensitive TreeMap is used
152 // so that even if the client provides a name with different case, the KDC
153 // can still locate the principal and give back correct salt.
154 private TreeMap<String,char[]> passwords = new TreeMap<>
155 (String.CASE_INSENSITIVE_ORDER);
156
157 // Non default salts. Precisely, there should be different salts for
158 // different etypes, pretend they are the same at the moment.
159 private TreeMap<String,String> salts = new TreeMap<>
160 (String.CASE_INSENSITIVE_ORDER);
161
162 // Non default s2kparams for newer etypes. Precisely, there should be
163 // different s2kparams for different etypes, pretend they are the same
164 // at the moment.
165 private TreeMap<String,byte[]> s2kparamses = new TreeMap<>
166 (String.CASE_INSENSITIVE_ORDER);
167
168 // Realm name
169 private String realm;
170 // KDC
171 private String kdc;
172 // Service port number
173 private int port;
174 // The request/response job queue
175 private BlockingQueue<Job> q = new ArrayBlockingQueue<>(100);
176 // Options
177 private Map<Option,Object> options = new HashMap<>();
178 // Realm-specific krb5.conf settings
179 private List<String> conf = new ArrayList<>();
180
181 private Thread thread1, thread2, thread3;
182 private volatile boolean udpConsumerReady = false;
183 private volatile boolean tcpConsumerReady = false;
184 private volatile boolean dispatcherReady = false;
185 DatagramSocket u1 = null;
186 ServerSocket t1 = null;
187
536 }
537 }
538 sb.append("\n[realms]\n");
539 sb.append(kdc.realmLine());
540 for (Object o : more) {
541 if (o instanceof KDC) {
542 sb.append(((KDC) o).realmLine());
543 }
544 }
545 Files.write(Paths.get(file), sb.toString().getBytes());
546 }
547
548 /**
549 * Returns the service port of the KDC server.
550 * @return the KDC service port
551 */
552 public int getPort() {
553 return port;
554 }
555
556 // Private helper methods
557
558 /**
559 * Private constructor, cannot be called outside.
560 * @param realm
561 */
562 private KDC(String realm, String kdc) {
563 this.realm = realm;
564 this.kdc = kdc;
565 this.nativeKdc = null;
566 }
567
568 /**
569 * A constructor that starts the KDC service also.
570 */
571 protected KDC(String realm, String kdc, int port, boolean asDaemon)
572 throws IOException {
573 this.realm = realm;
574 this.kdc = kdc;
575 this.nativeKdc = NativeKdc.get(this);
761 try {
762 System.out.println(realm + "> " + tgsReq.reqBody.cname +
763 " sends TGS-REQ for " +
764 service + ", " + tgsReq.reqBody.kdcOptions);
765 KDCReqBody body = tgsReq.reqBody;
766 int[] eTypes = filterSupported(KDCReqBodyDotEType(body));
767 if (eTypes.length == 0) {
768 throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
769 }
770 int e2 = eTypes[0]; // etype for outgoing session key
771 int e3 = eTypes[0]; // etype for outgoing ticket
772
773 PAData[] pas = KDCReqDotPAData(tgsReq);
774
775 Ticket tkt = null;
776 EncTicketPart etp = null;
777
778 PrincipalName cname = null;
779 boolean allowForwardable = true;
780
781 if (pas == null || pas.length == 0) {
782 throw new KrbException(Krb5.KDC_ERR_PADATA_TYPE_NOSUPP);
783 } else {
784 PrincipalName forUserCName = null;
785 for (PAData pa: pas) {
786 if (pa.getType() == Krb5.PA_TGS_REQ) {
787 APReq apReq = new APReq(pa.getValue());
788 EncryptedData ed = apReq.authenticator;
789 tkt = apReq.ticket;
790 int te = tkt.encPart.getEType();
791 EncryptionKey kkey = keyForUser(tkt.sname, te, true);
792 byte[] bb = tkt.encPart.decrypt(kkey, KeyUsage.KU_TICKET);
793 DerInputStream derIn = new DerInputStream(bb);
794 DerValue der = derIn.getDerValue();
795 etp = new EncTicketPart(der.toByteArray());
796 // Finally, cname will be overwritten by PA-FOR-USER
797 // if it exists.
798 cname = etp.cname;
799 System.out.println(realm + "> presenting a ticket of "
800 + etp.cname + " to " + tkt.sname);
947 }
948 Ticket t = new Ticket(
949 System.getProperty("test.kdc.diff.sname") != null ?
950 new PrincipalName("xx" + service.toString()) :
951 service,
952 new EncryptedData(skey, enc.asn1Encode(), KeyUsage.KU_TICKET)
953 );
954 EncTGSRepPart enc_part = new EncTGSRepPart(
955 key,
956 new LastReq(new LastReqEntry[] {
957 new LastReqEntry(0, timeAfter(-10))
958 }),
959 body.getNonce(), // TODO: detect replay
960 timeAfter(3600 * 24),
961 // Next 5 and last MUST be same with ticket
962 tFlags,
963 timeAfter(0),
964 from,
965 till, renewTill,
966 service,
967 body.addresses
968 );
969 EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(),
970 KeyUsage.KU_ENC_TGS_REP_PART_SESSKEY);
971 TGSRep tgsRep = new TGSRep(null,
972 cname,
973 t,
974 edata);
975 System.out.println(" Return " + tgsRep.cname
976 + " ticket for " + tgsRep.ticket.sname + ", flags "
977 + tFlags);
978
979 DerOutputStream out = new DerOutputStream();
980 out.write(DerValue.createTag(DerValue.TAG_APPLICATION,
981 true, (byte)Krb5.KRB_TGS_REP), tgsRep.asn1Encode());
982 return out.toByteArray();
983 } catch (KrbException ke) {
984 ke.printStackTrace(System.out);
985 KRBError kerr = ke.getError();
986 KDCReqBody body = tgsReq.reqBody;
987 System.out.println(" Error " + ke.returnCode()
991 timeAfter(0),
992 0,
993 ke.returnCode(),
994 body.cname,
995 service,
996 KrbException.errorMessage(ke.returnCode()),
997 null);
998 }
999 return kerr.asn1Encode();
1000 }
1001 }
1002
1003 /**
1004 * Processes a AS_REQ and generates a AS_REP (or KRB_ERROR)
1005 * @param in the request
1006 * @return the response
1007 * @throws java.lang.Exception for various errors
1008 */
1009 protected byte[] processAsReq(byte[] in) throws Exception {
1010 ASReq asReq = new ASReq(in);
1011 int[] eTypes = null;
1012 List<PAData> outPAs = new ArrayList<>();
1013
1014 PrincipalName service = asReq.reqBody.sname;
1015 if (options.containsKey(KDC.Option.RESP_NT)) {
1016 service = new PrincipalName((int)options.get(KDC.Option.RESP_NT),
1017 service.getNameStrings(),
1018 Realm.getDefault());
1019 }
1020 try {
1021 System.out.println(realm + "> " + asReq.reqBody.cname +
1022 " sends AS-REQ for " +
1023 service + ", " + asReq.reqBody.kdcOptions);
1024
1025 KDCReqBody body = asReq.reqBody;
1026
1027 eTypes = filterSupported(KDCReqBodyDotEType(body));
1028 if (eTypes.length == 0) {
1029 throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
1030 }
1031 int eType = eTypes[0];
1032
1033 EncryptionKey ckey = keyForUser(body.cname, eType, false);
1034 EncryptionKey skey = keyForUser(service, eType, true);
1035
1036 if (options.containsKey(KDC.Option.ONLY_RC4_TGT)) {
1037 int tgtEType = EncryptedData.ETYPE_ARCFOUR_HMAC;
1038 boolean found = false;
1039 for (int i=0; i<eTypes.length; i++) {
1040 if (eTypes[i] == tgtEType) {
1041 found = true;
1042 break;
1043 }
1044 }
1045 if (!found) {
1046 throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
1047 }
1048 skey = keyForUser(service, tgtEType, true);
1049 }
1050 if (ckey == null) {
1051 throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
1052 }
1194 epas[i] == EncryptedData.ETYPE_ARCFOUR_HMAC ?
1195 null : getSalt(body.cname)
1196 ).asn1Encode());
1197 }
1198 }
1199 }
1200
1201 DerOutputStream eid;
1202 if (pas2 != null) {
1203 eid = new DerOutputStream();
1204 eid.putSequence(pas2);
1205 outPAs.add(new PAData(Krb5.PA_ETYPE_INFO2, eid.toByteArray()));
1206 }
1207 if (pas != null) {
1208 eid = new DerOutputStream();
1209 eid.putSequence(pas);
1210 outPAs.add(new PAData(Krb5.PA_ETYPE_INFO, eid.toByteArray()));
1211 }
1212
1213 PAData[] inPAs = KDCReqDotPAData(asReq);
1214 if (inPAs == null || inPAs.length == 0) {
1215 Object preauth = options.get(Option.PREAUTH_REQUIRED);
1216 if (preauth == null || preauth.equals(Boolean.TRUE)) {
1217 throw new KrbException(Krb5.KDC_ERR_PREAUTH_REQUIRED);
1218 }
1219 } else {
1220 try {
1221 EncryptedData data = newEncryptedData(
1222 new DerValue(inPAs[0].getValue()));
1223 EncryptionKey pakey
1224 = keyForUser(body.cname, data.getEType(), false);
1225 data.decrypt(pakey, KeyUsage.KU_PA_ENC_TS);
1226 } catch (Exception e) {
1227 KrbException ke = new KrbException(Krb5.KDC_ERR_PREAUTH_FAILED);
1228 ke.initCause(e);
1229 throw ke;
1230 }
1231 bFlags[Krb5.TKT_OPTS_PRE_AUTHENT] = true;
1232 }
1233
1234 TicketFlags tFlags = new TicketFlags(bFlags);
1235 EncTicketPart enc = new EncTicketPart(
1236 tFlags,
1237 key,
1238 body.cname,
1239 new TransitedEncoding(1, new byte[0]),
1240 timeAfter(0),
1241 from,
1242 till, rtime,
1243 body.addresses,
1244 null);
1245 Ticket t = new Ticket(
1246 service,
1247 new EncryptedData(skey, enc.asn1Encode(), KeyUsage.KU_TICKET)
1248 );
1249 EncASRepPart enc_part = new EncASRepPart(
1250 key,
1251 new LastReq(new LastReqEntry[]{
1252 new LastReqEntry(0, timeAfter(-10))
1253 }),
1254 body.getNonce(), // TODO: detect replay?
1255 timeAfter(3600 * 24),
1256 // Next 5 and last MUST be same with ticket
1257 tFlags,
1258 timeAfter(0),
1259 from,
1260 till, rtime,
1261 service,
1262 body.addresses
1263 );
1264 EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(),
1265 KeyUsage.KU_ENC_AS_REP_PART);
1266 ASRep asRep = new ASRep(
1267 outPAs.toArray(new PAData[outPAs.size()]),
1268 body.cname,
1269 t,
1270 edata);
1271
1272 System.out.println(" Return " + asRep.cname
1273 + " ticket for " + asRep.ticket.sname + ", flags "
1274 + tFlags);
1275
1276 DerOutputStream out = new DerOutputStream();
1277 out.write(DerValue.createTag(DerValue.TAG_APPLICATION,
1278 true, (byte)Krb5.KRB_AS_REP), asRep.asn1Encode());
1279 byte[] result = out.toByteArray();
1280
1281 // Added feature:
1282 // Write the current issuing TGT into a ccache file specified
1290 CredentialsCache.create(asReq.reqBody.cname, ccache);
1291 if (cache == null) {
1292 throw new IOException("Unable to create the cache file " +
1293 ccache);
1294 }
1295 cache.update(credentials);
1296 cache.save();
1297 }
1298
1299 return result;
1300 } catch (KrbException ke) {
1301 ke.printStackTrace(System.out);
1302 KRBError kerr = ke.getError();
1303 KDCReqBody body = asReq.reqBody;
1304 System.out.println(" Error " + ke.returnCode()
1305 + " " +ke.returnCodeMessage());
1306 byte[] eData = null;
1307 if (kerr == null) {
1308 if (ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED ||
1309 ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED) {
1310 DerOutputStream bytes = new DerOutputStream();
1311 bytes.write(new PAData(Krb5.PA_ENC_TIMESTAMP, new byte[0]).asn1Encode());
1312 for (PAData p: outPAs) {
1313 bytes.write(p.asn1Encode());
1314 }
1315 DerOutputStream temp = new DerOutputStream();
1316 temp.write(DerValue.tag_Sequence, bytes);
1317 eData = temp.toByteArray();
1318 }
1319 kerr = new KRBError(null, null, null,
1320 timeAfter(0),
1321 0,
1322 ke.returnCode(),
1323 body.cname,
1324 service,
1325 KrbException.errorMessage(ke.returnCode()),
1326 eData);
1327 }
1328 return kerr.asn1Encode();
1329 }
1330 }
1331
|
148
149 // Under the hood.
150
151 // Principal db. principal -> pass. A case-insensitive TreeMap is used
152 // so that even if the client provides a name with different case, the KDC
153 // can still locate the principal and give back correct salt.
154 private TreeMap<String,char[]> passwords = new TreeMap<>
155 (String.CASE_INSENSITIVE_ORDER);
156
157 // Non default salts. Precisely, there should be different salts for
158 // different etypes, pretend they are the same at the moment.
159 private TreeMap<String,String> salts = new TreeMap<>
160 (String.CASE_INSENSITIVE_ORDER);
161
162 // Non default s2kparams for newer etypes. Precisely, there should be
163 // different s2kparams for different etypes, pretend they are the same
164 // at the moment.
165 private TreeMap<String,byte[]> s2kparamses = new TreeMap<>
166 (String.CASE_INSENSITIVE_ORDER);
167
168 // Alias for referrals.
169 private TreeMap<String,KDC> aliasReferrals = new TreeMap<>
170 (String.CASE_INSENSITIVE_ORDER);
171
172 // Alias for local resolution.
173 private TreeMap<String,PrincipalName> alias2Principals = new TreeMap<>
174 (String.CASE_INSENSITIVE_ORDER);
175
176 // Realm name
177 private String realm;
178 // KDC
179 private String kdc;
180 // Service port number
181 private int port;
182 // The request/response job queue
183 private BlockingQueue<Job> q = new ArrayBlockingQueue<>(100);
184 // Options
185 private Map<Option,Object> options = new HashMap<>();
186 // Realm-specific krb5.conf settings
187 private List<String> conf = new ArrayList<>();
188
189 private Thread thread1, thread2, thread3;
190 private volatile boolean udpConsumerReady = false;
191 private volatile boolean tcpConsumerReady = false;
192 private volatile boolean dispatcherReady = false;
193 DatagramSocket u1 = null;
194 ServerSocket t1 = null;
195
544 }
545 }
546 sb.append("\n[realms]\n");
547 sb.append(kdc.realmLine());
548 for (Object o : more) {
549 if (o instanceof KDC) {
550 sb.append(((KDC) o).realmLine());
551 }
552 }
553 Files.write(Paths.get(file), sb.toString().getBytes());
554 }
555
556 /**
557 * Returns the service port of the KDC server.
558 * @return the KDC service port
559 */
560 public int getPort() {
561 return port;
562 }
563
564 /**
565 * Register an alias name to be referred to a different KDC for
566 * resolution, according to RFC 6806.
567 * @param alias Alias name (i.e. user@REALM.COM).
568 * @param referredKDC KDC to which the alias is referred for resolution.
569 */
570 public void registerAlias(String alias, KDC referredKDC) {
571 aliasReferrals.remove(alias);
572 aliasReferrals.put(alias, referredKDC);
573 }
574
575 /**
576 * Register an alias to be resolved to a Principal Name locally,
577 * according to RFC 6806.
578 * @param alias Alias name (i.e. user@REALM.COM).
579 * @param user Principal Name to which the alias is resolved.
580 */
581 public void registerAlias(String alias, String user)
582 throws RealmException {
583 alias2Principals.remove(alias);
584 alias2Principals.put(alias, new PrincipalName(user));
585 }
586
587 // Private helper methods
588
589 /**
590 * Private constructor, cannot be called outside.
591 * @param realm
592 */
593 private KDC(String realm, String kdc) {
594 this.realm = realm;
595 this.kdc = kdc;
596 this.nativeKdc = null;
597 }
598
599 /**
600 * A constructor that starts the KDC service also.
601 */
602 protected KDC(String realm, String kdc, int port, boolean asDaemon)
603 throws IOException {
604 this.realm = realm;
605 this.kdc = kdc;
606 this.nativeKdc = NativeKdc.get(this);
792 try {
793 System.out.println(realm + "> " + tgsReq.reqBody.cname +
794 " sends TGS-REQ for " +
795 service + ", " + tgsReq.reqBody.kdcOptions);
796 KDCReqBody body = tgsReq.reqBody;
797 int[] eTypes = filterSupported(KDCReqBodyDotEType(body));
798 if (eTypes.length == 0) {
799 throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
800 }
801 int e2 = eTypes[0]; // etype for outgoing session key
802 int e3 = eTypes[0]; // etype for outgoing ticket
803
804 PAData[] pas = KDCReqDotPAData(tgsReq);
805
806 Ticket tkt = null;
807 EncTicketPart etp = null;
808
809 PrincipalName cname = null;
810 boolean allowForwardable = true;
811
812 if (body.kdcOptions.get(KDCOptions.CANONICALIZE)) {
813 KDC referral = aliasReferrals.get(body.sname.getNameString());
814 if (referral != null) {
815 service = new PrincipalName(
816 PrincipalName.TGS_DEFAULT_SRV_NAME +
817 PrincipalName.NAME_COMPONENT_SEPARATOR_STR +
818 referral.getRealm(), PrincipalName.KRB_NT_SRV_INST,
819 this.getRealm());
820 }
821 }
822
823 if (pas == null || pas.length == 0) {
824 throw new KrbException(Krb5.KDC_ERR_PADATA_TYPE_NOSUPP);
825 } else {
826 PrincipalName forUserCName = null;
827 for (PAData pa: pas) {
828 if (pa.getType() == Krb5.PA_TGS_REQ) {
829 APReq apReq = new APReq(pa.getValue());
830 EncryptedData ed = apReq.authenticator;
831 tkt = apReq.ticket;
832 int te = tkt.encPart.getEType();
833 EncryptionKey kkey = keyForUser(tkt.sname, te, true);
834 byte[] bb = tkt.encPart.decrypt(kkey, KeyUsage.KU_TICKET);
835 DerInputStream derIn = new DerInputStream(bb);
836 DerValue der = derIn.getDerValue();
837 etp = new EncTicketPart(der.toByteArray());
838 // Finally, cname will be overwritten by PA-FOR-USER
839 // if it exists.
840 cname = etp.cname;
841 System.out.println(realm + "> presenting a ticket of "
842 + etp.cname + " to " + tkt.sname);
989 }
990 Ticket t = new Ticket(
991 System.getProperty("test.kdc.diff.sname") != null ?
992 new PrincipalName("xx" + service.toString()) :
993 service,
994 new EncryptedData(skey, enc.asn1Encode(), KeyUsage.KU_TICKET)
995 );
996 EncTGSRepPart enc_part = new EncTGSRepPart(
997 key,
998 new LastReq(new LastReqEntry[] {
999 new LastReqEntry(0, timeAfter(-10))
1000 }),
1001 body.getNonce(), // TODO: detect replay
1002 timeAfter(3600 * 24),
1003 // Next 5 and last MUST be same with ticket
1004 tFlags,
1005 timeAfter(0),
1006 from,
1007 till, renewTill,
1008 service,
1009 body.addresses,
1010 null
1011 );
1012 EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(),
1013 KeyUsage.KU_ENC_TGS_REP_PART_SESSKEY);
1014 TGSRep tgsRep = new TGSRep(null,
1015 cname,
1016 t,
1017 edata);
1018 System.out.println(" Return " + tgsRep.cname
1019 + " ticket for " + tgsRep.ticket.sname + ", flags "
1020 + tFlags);
1021
1022 DerOutputStream out = new DerOutputStream();
1023 out.write(DerValue.createTag(DerValue.TAG_APPLICATION,
1024 true, (byte)Krb5.KRB_TGS_REP), tgsRep.asn1Encode());
1025 return out.toByteArray();
1026 } catch (KrbException ke) {
1027 ke.printStackTrace(System.out);
1028 KRBError kerr = ke.getError();
1029 KDCReqBody body = tgsReq.reqBody;
1030 System.out.println(" Error " + ke.returnCode()
1034 timeAfter(0),
1035 0,
1036 ke.returnCode(),
1037 body.cname,
1038 service,
1039 KrbException.errorMessage(ke.returnCode()),
1040 null);
1041 }
1042 return kerr.asn1Encode();
1043 }
1044 }
1045
1046 /**
1047 * Processes a AS_REQ and generates a AS_REP (or KRB_ERROR)
1048 * @param in the request
1049 * @return the response
1050 * @throws java.lang.Exception for various errors
1051 */
1052 protected byte[] processAsReq(byte[] in) throws Exception {
1053 ASReq asReq = new ASReq(in);
1054 byte[] asReqbytes = asReq.asn1Encode();
1055 int[] eTypes = null;
1056 List<PAData> outPAs = new ArrayList<>();
1057
1058 PrincipalName service = asReq.reqBody.sname;
1059 if (options.containsKey(KDC.Option.RESP_NT)) {
1060 service = new PrincipalName((int)options.get(KDC.Option.RESP_NT),
1061 service.getNameStrings(),
1062 Realm.getDefault());
1063 }
1064 try {
1065 System.out.println(realm + "> " + asReq.reqBody.cname +
1066 " sends AS-REQ for " +
1067 service + ", " + asReq.reqBody.kdcOptions);
1068
1069 KDCReqBody body = asReq.reqBody;
1070
1071 eTypes = filterSupported(KDCReqBodyDotEType(body));
1072 if (eTypes.length == 0) {
1073 throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
1074 }
1075 int eType = eTypes[0];
1076
1077 if (body.kdcOptions.get(KDCOptions.CANONICALIZE) &&
1078 body.cname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) {
1079 PrincipalName principal = alias2Principals.get(
1080 body.cname.getNameString());
1081 if (principal != null) {
1082 body.cname = principal;
1083 } else {
1084 KDC referral = aliasReferrals.get(body.cname.getNameString());
1085 if (referral != null) {
1086 body.cname = new PrincipalName(
1087 PrincipalName.TGS_DEFAULT_SRV_NAME,
1088 PrincipalName.KRB_NT_SRV_INST,
1089 referral.getRealm());
1090 throw new KrbException(Krb5.KRB_ERR_WRONG_REALM);
1091 }
1092 }
1093 }
1094
1095 EncryptionKey ckey = keyForUser(body.cname, eType, false);
1096 EncryptionKey skey = keyForUser(service, eType, true);
1097
1098 if (options.containsKey(KDC.Option.ONLY_RC4_TGT)) {
1099 int tgtEType = EncryptedData.ETYPE_ARCFOUR_HMAC;
1100 boolean found = false;
1101 for (int i=0; i<eTypes.length; i++) {
1102 if (eTypes[i] == tgtEType) {
1103 found = true;
1104 break;
1105 }
1106 }
1107 if (!found) {
1108 throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
1109 }
1110 skey = keyForUser(service, tgtEType, true);
1111 }
1112 if (ckey == null) {
1113 throw new KrbException(Krb5.KDC_ERR_ETYPE_NOSUPP);
1114 }
1256 epas[i] == EncryptedData.ETYPE_ARCFOUR_HMAC ?
1257 null : getSalt(body.cname)
1258 ).asn1Encode());
1259 }
1260 }
1261 }
1262
1263 DerOutputStream eid;
1264 if (pas2 != null) {
1265 eid = new DerOutputStream();
1266 eid.putSequence(pas2);
1267 outPAs.add(new PAData(Krb5.PA_ETYPE_INFO2, eid.toByteArray()));
1268 }
1269 if (pas != null) {
1270 eid = new DerOutputStream();
1271 eid.putSequence(pas);
1272 outPAs.add(new PAData(Krb5.PA_ETYPE_INFO, eid.toByteArray()));
1273 }
1274
1275 PAData[] inPAs = KDCReqDotPAData(asReq);
1276 List<PAData> enc_outPAs = new ArrayList<>();
1277 if (inPAs == null || inPAs.length == 0) {
1278 Object preauth = options.get(Option.PREAUTH_REQUIRED);
1279 if (preauth == null || preauth.equals(Boolean.TRUE)) {
1280 throw new KrbException(Krb5.KDC_ERR_PREAUTH_REQUIRED);
1281 }
1282 } else {
1283 EncryptionKey pakey = null;
1284 try {
1285 EncryptedData data = newEncryptedData(
1286 new DerValue(inPAs[0].getValue()));
1287 pakey = keyForUser(body.cname, data.getEType(), false);
1288 data.decrypt(pakey, KeyUsage.KU_PA_ENC_TS);
1289 } catch (Exception e) {
1290 KrbException ke = new KrbException(Krb5.KDC_ERR_PREAUTH_FAILED);
1291 ke.initCause(e);
1292 throw ke;
1293 }
1294 bFlags[Krb5.TKT_OPTS_PRE_AUTHENT] = true;
1295 for (PAData pa : inPAs) {
1296 if (pa.getType() == Krb5.PA_REQ_ENC_PA_REP) {
1297 Checksum ckSum = new Checksum(
1298 Checksum.CKSUMTYPE_HMAC_SHA1_96_AES128,
1299 asReqbytes, ckey, KeyUsage.KU_AS_REQ);
1300 enc_outPAs.add(new PAData(Krb5.PA_REQ_ENC_PA_REP,
1301 ckSum.asn1Encode()));
1302 bFlags[Krb5.TKT_OPTS_ENC_PA_REP] = true;
1303 break;
1304 }
1305 }
1306 }
1307
1308 TicketFlags tFlags = new TicketFlags(bFlags);
1309 EncTicketPart enc = new EncTicketPart(
1310 tFlags,
1311 key,
1312 body.cname,
1313 new TransitedEncoding(1, new byte[0]),
1314 timeAfter(0),
1315 from,
1316 till, rtime,
1317 body.addresses,
1318 null);
1319 Ticket t = new Ticket(
1320 service,
1321 new EncryptedData(skey, enc.asn1Encode(), KeyUsage.KU_TICKET)
1322 );
1323 EncASRepPart enc_part = new EncASRepPart(
1324 key,
1325 new LastReq(new LastReqEntry[]{
1326 new LastReqEntry(0, timeAfter(-10))
1327 }),
1328 body.getNonce(), // TODO: detect replay?
1329 timeAfter(3600 * 24),
1330 // Next 5 and last MUST be same with ticket
1331 tFlags,
1332 timeAfter(0),
1333 from,
1334 till, rtime,
1335 service,
1336 body.addresses,
1337 enc_outPAs.toArray(new PAData[enc_outPAs.size()])
1338 );
1339 EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(),
1340 KeyUsage.KU_ENC_AS_REP_PART);
1341 ASRep asRep = new ASRep(
1342 outPAs.toArray(new PAData[outPAs.size()]),
1343 body.cname,
1344 t,
1345 edata);
1346
1347 System.out.println(" Return " + asRep.cname
1348 + " ticket for " + asRep.ticket.sname + ", flags "
1349 + tFlags);
1350
1351 DerOutputStream out = new DerOutputStream();
1352 out.write(DerValue.createTag(DerValue.TAG_APPLICATION,
1353 true, (byte)Krb5.KRB_AS_REP), asRep.asn1Encode());
1354 byte[] result = out.toByteArray();
1355
1356 // Added feature:
1357 // Write the current issuing TGT into a ccache file specified
1365 CredentialsCache.create(asReq.reqBody.cname, ccache);
1366 if (cache == null) {
1367 throw new IOException("Unable to create the cache file " +
1368 ccache);
1369 }
1370 cache.update(credentials);
1371 cache.save();
1372 }
1373
1374 return result;
1375 } catch (KrbException ke) {
1376 ke.printStackTrace(System.out);
1377 KRBError kerr = ke.getError();
1378 KDCReqBody body = asReq.reqBody;
1379 System.out.println(" Error " + ke.returnCode()
1380 + " " +ke.returnCodeMessage());
1381 byte[] eData = null;
1382 if (kerr == null) {
1383 if (ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED ||
1384 ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED) {
1385 outPAs.add(new PAData(Krb5.PA_ENC_TIMESTAMP, new byte[0]));
1386 }
1387 if (outPAs.size() > 0) {
1388 DerOutputStream bytes = new DerOutputStream();
1389 for (PAData p: outPAs) {
1390 bytes.write(p.asn1Encode());
1391 }
1392 DerOutputStream temp = new DerOutputStream();
1393 temp.write(DerValue.tag_Sequence, bytes);
1394 eData = temp.toByteArray();
1395 }
1396 kerr = new KRBError(null, null, null,
1397 timeAfter(0),
1398 0,
1399 ke.returnCode(),
1400 body.cname,
1401 service,
1402 KrbException.errorMessage(ke.returnCode()),
1403 eData);
1404 }
1405 return kerr.asn1Encode();
1406 }
1407 }
1408
|