Author: erodriguez Date: Sat Oct 2 09:52:35 2004 New Revision: 51808 Added: incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/AuthenticationService.java incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/DefaultConfig.java incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/KdcDispatcher.java incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/KerberosException.java incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/RealmException.java incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/TicketGrantingService.java Log: core kerberos services and dispatcher
Added: incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/AuthenticationService.java ============================================================================== --- (empty file) +++ incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/AuthenticationService.java Sat Oct 2 09:52:35 2004 @@ -0,0 +1,248 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.kerberos.kdc; + +import org.apache.kerberos.crypto.*; +import org.apache.kerberos.io.encoder.*; +import org.apache.kerberos.messages.*; +import org.apache.kerberos.messages.components.*; +import org.apache.kerberos.messages.value.*; +import org.apache.kerberos.util.keytab.*; + +public class AuthenticationService { + + private KeyList _keytab; + + public AuthenticationService(KeyList keytab) { + _keytab = keytab; + } + + public AuthenticationReply getReplyFor(KdcRequest request) throws KeytabException, KerberosException { + + Realm realm = request.getRealm(); + + PrincipalName client = request.getCname(); + client.setRealm(realm); + EncryptionKey clientKey = _keytab.getEncryptionKey(client); + + PrincipalName server = request.getSname(); + server.setRealm(realm); + EncryptionKey serverKey = _keytab.getEncryptionKey(server); + + verifyPreAuthentication(request, client); + + Ticket ticket = getTicket(request); + encryptTicketPart(ticket, serverKey); + AuthenticationReply reply = getAuthenticationReply(request, ticket); + encryptReplyPart(reply, clientKey); + System.out.print("Got request from client " + client.toString() + " "); + System.out.println("for access to " + server.toString()); + return reply; + } + + // TODO - currently no support for pre-auth; requires server store support + private void verifyPreAuthentication(KdcRequest request, PrincipalName client) { + /* + if(client.pa_enc_timestamp_required and + pa_enc_timestamp not present) then + error_out(KDC_ERR_PREAUTH_REQUIRED(PA_ENC_TIMESTAMP)); + endif + */ + + /* + if(pa_enc_timestamp present) then + decrypt req.padata-value into decrypted_enc_timestamp + using client.key; + using auth_hdr.authenticator.subkey; + if (decrypt_error()) then + error_out(KRB_AP_ERR_BAD_INTEGRITY); + if(decrypted_enc_timestamp is not within allowable + skew) then error_out(KDC_ERR_PREAUTH_FAILED); + endif + if(decrypted_enc_timestamp and usec is replay) + error_out(KDC_ERR_PREAUTH_FAILED); + endif + add decrypted_enc_timestamp and usec to replay cache; + endif + */ + + /* + if (LocalConfig.DEFAULT_PA_ENC_TIMESTAMP_REQUIRED) { + byte[] encTimeStamp = CryptoService.getEncryptedTimestamp(key, new Date()); + if (key != null) { + paData = new PreAuthenticationData[1]; + paData[0] = new PreAuthenticationData(PreAuthenticationData.PA_ENC_TIMESTAMP, encTimeStamp); + } + } + */ + } + + // TODO - client and server parameters; requires store + private Ticket getTicket(KdcRequest request) throws KerberosException { + + Ticket newTicket = new Ticket(); + newTicket.setTicketVersionNumber(LocalConfig.TICKET_VNO); + + newTicket.setServerName(request.getSname()); + newTicket.setRealm(request.getRealm()); + + if(request.getKdcOptions().get(KdcOptions.FORWARDABLE)) + newTicket.getFlags().set(TicketFlags.FORWARDABLE); + + if(request.getKdcOptions().get(KdcOptions.PROXIABLE)) + newTicket.getFlags().set(TicketFlags.PROXIABLE); + + if(request.getKdcOptions().get(KdcOptions.ALLOW_POSTDATE)) + newTicket.getFlags().set(TicketFlags.MAY_POSTDATE); + + if(request.getKdcOptions().get(KdcOptions.RENEW) || + request.getKdcOptions().get(KdcOptions.VALIDATE) || + request.getKdcOptions().get(KdcOptions.PROXY) || + request.getKdcOptions().get(KdcOptions.FORWARDED) || + request.getKdcOptions().get(KdcOptions.ENC_TKT_IN_SKEY)) + throw KerberosException.KDC_ERR_BADOPTION; + + newTicket.setSessionKey(CryptoService.getNewSessionKey()); + newTicket.setClientName(request.getCname()); + newTicket.setClientRealm(request.getRealm()); + newTicket.setTransitedEncoding(new TransitedEncoding()); + + KerberosTime now = new KerberosTime(); + newTicket.setAuthtime(now); + + if(request.getKdcOptions().get(KdcOptions.POSTDATED)) { + // TODO - possibly allow req.from range + if (!LocalConfig.KDC_POSTDATE_ALLOWED) + throw KerberosException.KDC_ERR_POLICY; + newTicket.setFlag(TicketFlags.INVALID); + newTicket.setStartTime(request.getFrom()); + } + + long till = 0; + if (request.getTill().getTime() == 0) + till = Long.MAX_VALUE; + else + till = request.getTill().getTime(); + /* + new_tkt.endtime := min(till, + new_tkt.starttime+client.max_life, + new_tkt.starttime+server.max_life, + new_tkt.starttime+max_life_for_realm); + */ + long endTime = Math.min(now.getTime() + LocalConfig.DEFAULT_MAXIMUM_TICKET_LIFETIME, till); + newTicket.setEndTime(new KerberosTime(endTime)); + + long tempRtime = 0; + if (request.getKdcOptions().get(KdcOptions.RENEWABLE_OK) && + request.getTill().greaterThan(newTicket.getEndTime())) { + request.getKdcOptions().set(KdcOptions.RENEWABLE); + tempRtime = request.getTill().getTime(); + } + + /* + if (req.kdc-options.RENEWABLE is set) then + set new_tkt.flags.RENEWABLE; + new_tkt.renew-till := min(rtime, + new_tkt.starttime+client.max_rlife, + new_tkt.starttime+server.max_rlife, + new_tkt.starttime+max_rlife_for_realm); + else + omit new_tkt.renew-till; + endif + */ + if (tempRtime == 0) + tempRtime = Long.MAX_VALUE; + else + tempRtime = request.getRtime().getTime(); + + if (request.getKdcOptions().get(KdcOptions.RENEWABLE)) { + newTicket.getFlags().set(TicketFlags.RENEWABLE); + long renewTill = Math.min(newTicket.getStartTime().getTime() + + LocalConfig.DEFAULT_MAXIMUM_RENEWABLE_LIFETIME, tempRtime); + newTicket.setRenewTill(new KerberosTime(renewTill)); + } + + if (request.getAddresses() != null) + newTicket.setClientAddresses(request.getAddresses()); + return newTicket; + } + + // TODO - support multiple encryption types + private void encryptTicketPart(Ticket ticket, EncryptionKey serverKey) { + EncTicketPartEncoder encoder = new EncTicketPartEncoder(); + + try { + byte[] plainText = encoder.encode(ticket); + + CryptoService enc = new CryptoService(); + + EncryptedData cipherText = enc.getEncryptedData(serverKey, plainText); + + ticket.setEncPart(cipherText); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + // TODO - support multiple encryption types + private void encryptReplyPart(AuthenticationReply reply, EncryptionKey clientKey) { + EncAsRepPartEncoder encoder = new EncAsRepPartEncoder(); + try { + byte[] plainText = encoder.encode(reply); + + CryptoService enc = new CryptoService(); + + EncryptedData cipherText = enc.getEncryptedData(clientKey, plainText); + + reply.setEncPart(cipherText); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + private AuthenticationReply getAuthenticationReply(KdcRequest request, Ticket ticket) { + AuthenticationReply reply = new AuthenticationReply(); + + reply.setCname(request.getCname()); + reply.setCrealm(request.getRealm()); + reply.setTicket(ticket); + reply.setKey(ticket.getSessionKey()); + + // TODO - fetch lastReq for this client; requires store + reply.setLastRequest(new LastRequest()); + // TODO - resp.key-expiration := client.expiration; requires store + + reply.setNonce(request.getNonce()); + + reply.setFlags(ticket.getFlags()); + reply.setAuthTime(ticket.getAuthtime()); + reply.setStartTime(ticket.getStartTime()); + reply.setEndTime(ticket.getEndTime()); + + if (ticket.getFlags().get(TicketFlags.RENEWABLE)) + reply.setRenewTill(ticket.getRenewTill()); + + reply.setServerRealm(ticket.getRealm()); + reply.setServerName(ticket.getServerName()); + reply.setClientAddresses(ticket.getClientAddresses()); + + return reply; + } +} + Added: incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/DefaultConfig.java ============================================================================== --- (empty file) +++ incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/DefaultConfig.java Sat Oct 2 09:52:35 2004 @@ -0,0 +1,86 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.kerberos.kdc; + +import org.apache.kerberos.crypto.encryption.*; +import org.apache.kerberos.kdc.replay.*; +import org.apache.kerberos.messages.value.*; +import org.apache.kerberos.util.keytab.*; + +public class DefaultConfig { + + // Default inet port + public static final int KDC_INET_DEFAULT_PORT = 88; + // Default server name + public static final String TGS_DEFAULT_SRV_NAME = "krbtgt"; + // Default NT + public static final int TGS_DEFAULT_NT = PrincipalName.KRB_NT_SRV_INST; + + public static final ReplayCache REPLAY_CACHE = new InMemoryReplayCache(); + + // Default encryption type + public static final EncryptionType DEFAULT_ETYPE = EncryptionType.DES_CBC_MD5; + + // Default encryption type list + public static final EncryptionType[] DEFAULT_ETYPE_LIST = { EncryptionType.DES_CBC_MD5, + EncryptionType.DES_CBC_MD4, EncryptionType.DES_CBC_CRC + }; + + // Time constants + public static final long MINUTE = 60 * 1000; + public static final long DAY = 24 * 60 * MINUTE; + public static final long WEEK = 7 * DAY; + // Default allowable clock skew + public static final long DEFAULT_ALLOWABLE_CLOCKSKEW = 5 * MINUTE; + // Default minimum lifetime + public static final long DEFAULT_MINIMUM_LIFETIME = 5 * MINUTE; + // Default maximum renewable lifetime + public static final long DEFAULT_MAXIMUM_RENEWABLE_LIFETIME = 1 * WEEK; + // Default maximum ticket lifetime + public static final long DEFAULT_MAXIMUM_TICKET_LIFETIME = 1 * DAY; + + // Site specific values + // Whether to allow empty address + public static final boolean DEFAULT_EMPTY_ADDRESSES_ALLOWED = true; + // Whether forwarding is allowed + public static final boolean DEFAULT_FORWARDABLE_ALLOWED = true; + // Whether to allow proxy + public static final boolean DEFAULT_PROXIABLE_ALLOWED = true; + // Whether to allow postdated credentials + public static final boolean DEFAULT_POSTDATE_ALLOWED = true; + // Whether to allow renewable credentials + public static final boolean DEFAULT_RENEWABLE_ALLOWED = true; + // DEFAULT_PA_ENC_TIMESTAMP_REQUIRED = true + public static final boolean DEFAULT_PA_ENC_TIMESTAMP_REQUIRED = true; + // DEFAULT_AP_EMPTY_ADDRESSES_ALLOWED = true + public static final boolean DEFAULT_AP_EMPTY_ADDRESSES_ALLOWED = true; + + // Protocol constants and associated values + // Transited encoding type - Domain x500 compress + public static final int DOMAIN_X500_COMPRESS = 1; + + // Kerberos protocol version number + public static final int PVNO = 5; + // Authenticator version number + public static final int AUTHENTICATOR_VNO = 5; + // Ticket version number + public static final int TICKET_VNO = 5; + // Default keytab version number + public static final int KT_VNO = Keytab.VNO_2; + +} + Added: incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/KdcDispatcher.java ============================================================================== --- (empty file) +++ incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/KdcDispatcher.java Sat Oct 2 09:52:35 2004 @@ -0,0 +1,94 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.kerberos.kdc; + +import org.apache.kerberos.io.decoder.*; +import org.apache.kerberos.io.encoder.*; +import org.apache.kerberos.kdc.replay.*; +import org.apache.kerberos.messages.*; +import org.apache.kerberos.util.*; +import org.apache.kerberos.util.keytab.*; + +import java.io.*; + +public class KdcDispatcher { + + private static final byte AS_REQ = (byte) 0x6A; + private static final byte AS_REP = (byte) 0x6B; + private static final byte TGS_REQ = (byte) 0x6C; + private static final byte TGS_REP = (byte) 0x6D; + + private static final ReplayCache replay = LocalConfig.REPLAY_CACHE; + + private static final KdcRequestDecoder decoder = new KdcRequestDecoder(); + private static final KdcReplyEncoder encoder = new KdcReplyEncoder(); + + private AuthenticationService _authService; + private TicketGrantingService _tgsService; + private KeyList _store; + + public KdcDispatcher(KeyList store) { + _store = store; + _authService = new AuthenticationService(_store); + _tgsService = new TicketGrantingService(_store, replay); + } + + public byte[] dispatch(byte[] requestBytes) throws IOException, KerberosException, KeytabException { + + TestUtils.hexdump(requestBytes); + + ByteArrayInputStream input = new ByteArrayInputStream(requestBytes); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + + KdcRequest request = decoder.decode(input); + + byte messageType = requestBytes[0]; + + switch (messageType) { + + case AS_REQ: + // generate the reply + AuthenticationReply authReply = _authService.getReplyFor(request); + // ASN1 encode the reply + encoder.encode(authReply, output); + + break; + + case TGS_REQ: + // generate the reply + TicketGrantReply ticketReply = _tgsService.getReplyFor(request); + // ASN1 encode the reply + encoder.encode(ticketReply, output); + + break; + + case AS_REP: + case TGS_REP: + throw new IOException("We should not be receiving reply messages"); + + default: + System.out.println("Message received with tag " + messageType); + } + + byte[] replyBytes = output.toByteArray(); + + TestUtils.hexdump(replyBytes); + + return replyBytes; + } +} + Added: incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/KerberosException.java ============================================================================== --- (empty file) +++ incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/KerberosException.java Sat Oct 2 09:52:35 2004 @@ -0,0 +1,175 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.kerberos.kdc; + +public class KerberosException extends Exception { + + public static final KerberosException KDC_ERR_NONE = new KerberosException(0, + "No error"); + public static final KerberosException KDC_ERR_NAME_EXP = new KerberosException(1, + "Client's entry in database expired"); + public static final KerberosException KDC_ERR_SERVICE_EXP = new KerberosException(2, + "Server's entry in database has expired"); + public static final KerberosException KDC_ERR_BAD_PVNO = new KerberosException(3, + "Requested protocol version number not supported"); + public static final KerberosException KDC_ERR_C_OLD_MAST_KVNO = new KerberosException(4, + "Client's key encrypted in old master key"); + public static final KerberosException KDC_ERR_S_OLD_MAST_KVNO = new KerberosException(5, + "Server's key encrypted in old master key"); + public static final KerberosException KDC_ERR_C_PRINCIPAL_UNKNOWN = new KerberosException(6, + "Client not found in Kerberos database"); + public static final KerberosException KDC_ERR_S_PRINCIPAL_UNKNOWN = new KerberosException(7, + "Server not found in Kerberos database"); + public static final KerberosException KDC_ERR_PRINCIPAL_NOT_UNIQUE = new KerberosException(8, + "Multiple principal entries in database"); + public static final KerberosException KDC_ERR_NULL_KEY = new KerberosException(9, + "The client or server has a null key"); + public static final KerberosException KDC_ERR_CANNOT_POSTDATE = new KerberosException(10, + "Ticket not eligible for postdating"); + public static final KerberosException KDC_ERR_NEVER_VALID = new KerberosException(11, + "Requested start time is later than end time"); + public static final KerberosException KDC_ERR_POLICY = new KerberosException(12, + "KDC policy rejects request"); + public static final KerberosException KDC_ERR_BADOPTION = new KerberosException(13, + "KDC cannot accommodate requested option"); + public static final KerberosException KDC_ERR_ETYPE_NOSUPP = new KerberosException(14, + "KDC has no support for encryption type"); + public static final KerberosException KDC_ERR_SUMTYPE_NOSUPP = new KerberosException(15, + "KDC has no support for checksum type"); + public static final KerberosException KDC_ERR_PADATA_TYPE_NOSUPP = new KerberosException(16, + "KDC has no support for padata type"); + public static final KerberosException KDC_ERR_TRTYPE_NOSUPP = new KerberosException(17, + "KDC has no support for transitedEncoding type"); + public static final KerberosException KDC_ERR_CLIENT_REVOKED = new KerberosException(18, + "Clients credentials have been revoked"); + public static final KerberosException KDC_ERR_SERVICE_REVOKED = new KerberosException(19, + "Credentials for server have been revoked"); + public static final KerberosException KDC_ERR_TGT_REVOKED = new KerberosException(20, + "TGT has been revoked"); + public static final KerberosException KDC_ERR_CLIENT_NOTYET = new KerberosException(21, + "Client not yet valid - try again later"); + public static final KerberosException KDC_ERR_SERVICE_NOTYET = new KerberosException(22, + "Server not yet valid - try again later"); + public static final KerberosException KDC_ERR_KEY_EXPIRED = new KerberosException(23, + "Password has expired - change password to reset"); + public static final KerberosException KDC_ERR_PREAUTH_FAILED = new KerberosException(24, + "Pre-authentication information was invalid"); + public static final KerberosException KDC_ERR_PREAUTH_REQUIRED = new KerberosException(25, + "Additional pre-authentication required"); + public static final KerberosException KDC_ERR_SERVER_NOMATCH = new KerberosException(26, + "Requested server and ticket don't match"); + public static final KerberosException KDC_ERR_MUST_USE_USER2USER = new KerberosException(27, + "Server valid for user2user only"); + public static final KerberosException KDC_ERR_PATH_NOT_ACCEPTED = new KerberosException(28, + "KDC Policy rejects transitedEncoding path"); + public static final KerberosException KDC_ERR_SVC_UNAVAILABLE = new KerberosException(29, + "A service is not available"); + public static final KerberosException KRB_AP_ERR_BAD_INTEGRITY = new KerberosException(31, + "Integrity check on decrypted field failed"); + public static final KerberosException KRB_AP_ERR_TKT_EXPIRED = new KerberosException(32, + "Ticket expired"); + public static final KerberosException KRB_AP_ERR_TKT_NYV = new KerberosException(33, + "Ticket not yet valid"); + public static final KerberosException KRB_AP_ERR_REPEAT = new KerberosException(34, + "Request is a replay"); + public static final KerberosException KRB_AP_ERR_NOT_US = new KerberosException(35, + "The ticket isn't for us"); + public static final KerberosException KRB_AP_ERR_BADMATCH = new KerberosException(36, + "Ticket and authenticator don't match"); + public static final KerberosException KRB_AP_ERR_SKEW = new KerberosException(37, + "Clock skew too great"); + public static final KerberosException KRB_AP_ERR_BADADDR = new KerberosException(38, + "Incorrect net address"); + public static final KerberosException KRB_AP_ERR_BADVERSION = new KerberosException(39, + "Protocol version mismatch"); + public static final KerberosException KRB_AP_ERR_MSG_TYPE = new KerberosException(40, + "Invalid msg type"); + public static final KerberosException KRB_AP_ERR_MODIFIED = new KerberosException(41, + "Message stream modified"); + public static final KerberosException KRB_AP_ERR_BADORDER = new KerberosException(42, + "Message out of order"); + public static final KerberosException KRB_AP_ERR_BADKEYVER = new KerberosException(44, + "Specified version of key is not available"); + public static final KerberosException KRB_AP_ERR_NOKEY = new KerberosException(45, + "Service key not available"); + public static final KerberosException KRB_AP_ERR_MUT_FAIL = new KerberosException(46, + "Mutual authentication failed"); + public static final KerberosException KRB_AP_ERR_BADDIRECTION = new KerberosException(47, + "Incorrect message direction"); + public static final KerberosException KRB_AP_ERR_METHOD = new KerberosException(48, + "Alternative authentication method required"); + public static final KerberosException KRB_AP_ERR_BADSEQ = new KerberosException(49, + "Incorrect sequence number in message"); + public static final KerberosException KRB_AP_ERR_INAPP_CKSUM = new KerberosException(50, + "Inappropriate type of checksum in message"); + public static final KerberosException KRB_ERR_GENERIC = new KerberosException(60, + "Generic error (description in e-text)"); + public static final KerberosException KRB_ERR_FIELD_TOOLONG = new KerberosException(61, + "Field is too long for this implementation"); + public static final KerberosException KRB_ERR_CLIENT_NOT_TRUSTED = new KerberosException(62, + "Client is not trusted"); + public static final KerberosException KRB_ERR_KDC_NOT_TRUSTED = new KerberosException(63, + "KDC is not trusted"); + public static final KerberosException KRB_ERR_INVALID_SIG = new KerberosException(64, + "Signature is invalid"); + public static final KerberosException KRB_ERR_KEY_TOO_WEAK = new KerberosException(65, + "Key too weak"); + public static final KerberosException KRB_ERR_CERTIFICATE_MISMATCH = new KerberosException(66, + "Certificates do not match"); + public static final KerberosException KRB_AP_ERR_NO_TGT = new KerberosException(67, + "No tgt for user-to-user authentication"); + public static final KerberosException KRB_ERR_WRONG_REALM = new KerberosException(68, + "Wrong realm"); + public static final KerberosException KRB_AP_ERR_USER_TO_USER_REQUIRED = new KerberosException( + 69, "User-to-user authentication required"); + public static final KerberosException KRB_ERR_CANT_VERIFY_CERTIFICATE = new KerberosException( + 70, "Can't verify certificate"); + public static final KerberosException KRB_ERR_INVALID_CERTIFICATE = new KerberosException(71, + "Invalid certificate"); + public static final KerberosException KRB_ERR_REVOKED_CERTIFICATE = new KerberosException(72, + "Revoked certificate"); + public static final KerberosException KRB_ERR_REVOCATION_STATUS_UNKNOWN = new KerberosException( + 73, "Revocation status unknown"); + public static final KerberosException KRB_ERR_REVOCATION_STATUS_UNAVAILABLE = new KerberosException( + 74, "Revocation status unavailable"); + public static final KerberosException KRB_ERR_CLIENT_NAME_MISMATCH = new KerberosException(75, + "Client names do not match"); + public static final KerberosException KRB_ERR_KDC_NAME_MISMATCH = new KerberosException(76, + "KDC names do not match"); + + public String toString() { + return _fName; + } + + public int getOrdinal() { + return _fOrdinal; + } + + /// PRIVATE ///// + private final String _fName; + private final int _fOrdinal; + + /** + * Private constructor prevents construction outside of this class. + */ + private KerberosException(int ordinal, String name) { + super(name); + _fOrdinal = ordinal; + _fName = name; + } +} + Added: incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/RealmException.java ============================================================================== --- (empty file) +++ incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/RealmException.java Sat Oct 2 09:52:35 2004 @@ -0,0 +1,49 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.kerberos.kdc; + +public class RealmException extends Exception { + + public static final RealmException REALM_ILLEGAL_CHAR = new RealmException(600, + "Illegal character in realm name; one of: '/', ':'"); + + public static final RealmException REALM_NULL_NAME = new RealmException(601, + "Null realm name"); + + + public String toString() { + return _fName; + } + + public int getOrdinal() { + return _fOrdinal; + } + + /// PRIVATE ///// + private final String _fName; + private final int _fOrdinal; + + /** + * Private constructor prevents construction outside of this class. + */ + private RealmException(int ordinal, String name) { + super(name); + _fOrdinal = ordinal; + _fName = name; + } +} + Added: incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/TicketGrantingService.java ============================================================================== --- (empty file) +++ incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/kdc/TicketGrantingService.java Sat Oct 2 09:52:35 2004 @@ -0,0 +1,566 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.kerberos.kdc; + +import org.apache.kerberos.crypto.*; +import org.apache.kerberos.crypto.checksum.*; +import org.apache.kerberos.crypto.encryption.*; +import org.apache.kerberos.io.decoder.*; +import org.apache.kerberos.io.encoder.*; +import org.apache.kerberos.kdc.replay.*; +import org.apache.kerberos.messages.*; +import org.apache.kerberos.messages.components.*; +import org.apache.kerberos.messages.value.*; +import org.apache.kerberos.util.keytab.*; + +import java.io.*; +import java.util.*; + +/** + * RFC 1510 A.6. KRB_TGS_REQ verification and KRB_TGS_REP generation + */ +public class TicketGrantingService { + + private KeyList _keytab; + private ReplayCache _replayCache; + + public TicketGrantingService(KeyList keytab, ReplayCache replay) { + _keytab = keytab; + _replayCache = replay; + } + + public TicketGrantReply getReplyFor(KdcRequest request) throws KerberosException, IOException, KeytabException { + + System.out.println("Got request from " + request.getCname() + "@" + request.getRealm()); + + ApplicationRequest authHeader = getAuthHeader(request); + + Ticket tgt = authHeader.getTicket(); + + Authenticator authenticator = verifyApReq(authHeader, tgt); + + verifyTicket(authHeader, request); + + Realm realm = tgt.getRealm(); + + verifyBodyChecksum(authenticator.getChecksum(), request); + + EncryptionKey serverKey = getServerKey(request); + + EncryptionKey sessionKey = CryptoService.getNewSessionKey(); + + EncryptionType eType = CryptoService.getBestEncryptionType(request.getEType()); + + Ticket newTicket = getNewTicket(request, tgt, realm, sessionKey, authenticator); + + processTimes(request, newTicket, tgt); + + processTransited(newTicket, tgt); + + encryptTicketPart(newTicket, serverKey, request); + + TicketGrantReply reply = getReply(tgt, newTicket, sessionKey, request); + + if (authenticator.getSubSessionKey() != null) { + System.out.println("Using authenticator sub session key."); + EncryptionKey subKey = authenticator.getSubSessionKey(); + encryptReplyPart(reply, subKey); + } else { + System.out.println("Using session key."); + encryptReplyPart(reply, tgt.getSessionKey()); + } + + return reply; + } + + /* + * Reading the application request requires first determining the server + * for which a ticket was issued, and choosing the correct key for decryption. + * The name of the server appears in the plaintext part of the ticket. + */ + private ApplicationRequest getAuthHeader(KdcRequest request) throws KerberosException, IOException { + + if (request.getPaData()[0].getDataType() != PreAuthenticationData.PA_TGS_REQ) + throw KerberosException.KDC_ERR_PADATA_TYPE_NOSUPP; + + byte[] undecodedAuthHeader = request.getPaData()[0].getDataValue(); + ApplicationRequestDecoder decoder = new ApplicationRequestDecoder(); + ApplicationRequest authHeader = decoder.decode(undecodedAuthHeader); + + return authHeader; + } + + // RFC 1510 A.10. KRB_AP_REQ verification + private Authenticator verifyApReq(ApplicationRequest authHeader, Ticket tgt) + throws KerberosException, IOException, KeytabException { + + if (authHeader.getProtocolVersionNumber() != 5) + throw KerberosException.KRB_AP_ERR_BADVERSION; + if (authHeader.getMessageType() != MessageType.KRB_AP_REQ) + throw KerberosException.KRB_AP_ERR_MSG_TYPE; + if (authHeader.getTicket().getTicketVersionNumber() != 5) + throw KerberosException.KRB_AP_ERR_BADVERSION; + + // TODO - support multiple encryption types + EncryptionKey serverKey = null; + if (authHeader.getOption(ApOptions.USE_SESSION_KEY)) { + serverKey = authHeader.getTicket().getSessionKey(); + } else { + PrincipalName server = tgt.getServerName(); + server.setRealm(tgt.getRealm()); + serverKey = _keytab.getEncryptionKey(server); + } + if (serverKey == null) { + // TODO - check server key version number, skvno; requires store + if (false) + throw KerberosException.KRB_AP_ERR_BADKEYVER; + + throw KerberosException.KRB_AP_ERR_NOKEY; + } + + CryptoService enc = new CryptoService(); + + try { + byte[] decTicketPart = enc.decrypt(serverKey, tgt.getEncPart()); + + EncTicketPartDecoder ticketPartDecoder = new EncTicketPartDecoder(); + EncTicketPart encPart = ticketPartDecoder.decode(decTicketPart); + tgt.setEncTicketPart(encPart); + } catch (KerberosException ke) { + throw KerberosException.KRB_AP_ERR_BAD_INTEGRITY; + } + + Authenticator authenticator; + + try { + byte[] decAuthenticator = enc.decrypt(tgt.getSessionKey(), authHeader.getEncPart()); + AuthenticatorDecoder authDecoder = new AuthenticatorDecoder(); + authenticator = authDecoder.decode(decAuthenticator); + } catch (KerberosException ke) { + throw KerberosException.KRB_AP_ERR_BAD_INTEGRITY; + } + + if (!authenticator.getClientName().equals(tgt.getClientName()) && + !authenticator.getClientRealm().equals(tgt.getClientRealm())) + throw KerberosException.KRB_AP_ERR_BADMATCH; + + // TODO - need to get at IP Address for sender + if (tgt.getClientAddresses() != null) { + // if (sender_address(packet) is not in decr_ticket.caddr) + // then error_out(KRB_AP_ERR_BADADDR); + } + else { + // if (application requires addresses) then + // error_out(KRB_AP_ERR_BADADDR); + } + + if(_replayCache.isReplay(authenticator.getClientTime(), authenticator.getClientName(), + authenticator.getClientRealm())) { + throw KerberosException.KRB_AP_ERR_REPEAT; + } + + _replayCache.save(authenticator.getClientTime(), authenticator.getClientName(), + authenticator.getClientRealm()); + + if (!authenticator.getClientTime().isInClockSkew()) + throw KerberosException.KRB_AP_ERR_SKEW; + + if (tgt.getStartTime() != null && !tgt.getStartTime().isInClockSkew() || + tgt.getFlag(TicketFlags.INVALID)) + // it hasn't yet become valid + throw KerberosException.KRB_AP_ERR_TKT_NYV; + + // TODO - doesn't take into account skew + if (!tgt.getEndTime().greaterThan(new KerberosTime())) + throw KerberosException.KRB_AP_ERR_TKT_EXPIRED; + + authHeader.setOption(ApOptions.MUTUAL_REQUIRED); + + return authenticator; + } + + /* + * Note that the realm in which the Kerberos server is operating is determined by + * the instance from the ticket-granting ticket. The realm in the ticket-granting + * ticket is the realm under which the ticket granting ticket was issued. It is + * possible for a single Kerberos server to support more than one realm. + */ + private void verifyTicket(ApplicationRequest authHeader, KdcRequest request) + throws KerberosException { + + Ticket tgt = authHeader.getTicket(); + if (!tgt.getRealm().toString().equals(LocalConfig.KDC_PRIMARY_REALM) && + !tgt.getServerName().equals(request.getSname())) + throw KerberosException.KRB_AP_ERR_NOT_US; + } + + // TODO - configurable checksum + private void verifyBodyChecksum(Checksum authChecksum, KdcRequest request) + throws KerberosException { + + if (authChecksum == null) + throw KerberosException.KRB_AP_ERR_INAPP_CKSUM; + + /* + if (auth_hdr.authenticator.cksum type is not supported) then + error_out(KDC_ERR_SUMTYPE_NOSUPP); + endif + */ + + /* + if (auth_hdr.authenticator.cksum is not both collision-proof and keyed) then + error_out(KRB_AP_ERR_INAPP_CKSUM); + endif + */ + + KdcReqBodyEncoder encoder = new KdcReqBodyEncoder(); + byte[] bytes = null; + try { + bytes = encoder.encode(request); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + + ChecksumEngine digester = CryptoService.getInstance(ChecksumType.RSA_MD5); + Checksum newChecksum = new Checksum(digester.checksumType(), digester.calculateChecksum(bytes)); + + boolean equal = newChecksum.equals(authChecksum); + + if (!equal) + throw KerberosException.KRB_AP_ERR_MODIFIED; + } + + private EncryptionKey getServerKey(KdcRequest request) throws KerberosException { + + EncryptionKey serverKey = null; + // TODO - allow lookup with realm + try { + PrincipalName server = request.getSname(); + server.setRealm(request.getRealm()); + System.out.println(server); + serverKey = _keytab.getEncryptionKey(server); + } catch (KeytabException ke) { + /* + if (!server) then + if (is_foreign_tgt_name(server)) then + server := best_intermediate_tgs(server); + else + // no server in Database + error_out(KDC_ERR_S_PRINCIPAL_UNKNOWN); + endif + endif + */ + throw KerberosException.KDC_ERR_S_PRINCIPAL_UNKNOWN; + } + return serverKey; + } + + private Ticket getNewTicket(KdcRequest request, Ticket tgt, + Realm realm, EncryptionKey sessionKey, Authenticator authenticator) + throws KerberosException { + + Ticket newTicket = new Ticket(); + newTicket.setTicketVersionNumber(LocalConfig.TICKET_VNO); + newTicket.setServerName(request.getSname()); + newTicket.setRealm(realm); + + /* + * Note that local policy may affect the processing of any of these flags. + * For example, some realms may refuse to issue renewable tickets + */ + newTicket.setClientAddresses(tgt.getClientAddresses()); + + processFlags(request, tgt, newTicket); + + newTicket.setSessionKey(sessionKey); + newTicket.setClientRealm(tgt.getClientRealm()); + newTicket.setClientName(tgt.getClientName()); + + AuthorizationData authData = processAuthorizationData(request, authenticator, tgt); + newTicket.setAuthorizationData(authData); + + return newTicket; + } + + private void processFlags(KdcRequest request, Ticket tgt, Ticket newTicket) + throws KerberosException { + + if (request.getOption(KdcOptions.FORWARDABLE)) { + if (!tgt.getFlag(TicketFlags.FORWARDABLE)) + throw KerberosException.KDC_ERR_BADOPTION; + newTicket.setFlag(TicketFlags.FORWARDABLE); + } + + if (request.getOption(KdcOptions.FORWARDED)) { + if (!tgt.getFlag(TicketFlags.FORWARDABLE)) + throw KerberosException.KDC_ERR_BADOPTION; + newTicket.setFlag(TicketFlags.FORWARDED); + newTicket.setClientAddresses(request.getAddresses()); + // reply.setClientAddresses(request.getClientAddresses()); moved to getReply + } + + if (tgt.getFlag(TicketFlags.FORWARDED)) + newTicket.setFlag(TicketFlags.FORWARDED); + + if (request.getOption(KdcOptions.PROXIABLE)) { + if (!tgt.getFlag(TicketFlags.PROXIABLE)) + throw KerberosException.KDC_ERR_BADOPTION; + newTicket.setFlag(TicketFlags.PROXIABLE); + } + + if (request.getOption(KdcOptions.PROXY)) { + if (!tgt.getFlag(TicketFlags.PROXIABLE)) + throw KerberosException.KDC_ERR_BADOPTION; + newTicket.setFlag(TicketFlags.PROXY); + newTicket.setClientAddresses(request.getAddresses()); + // reply.setClientAddresses(request.getClientAddresses()); moved to getReply + } + + if (request.getOption(KdcOptions.ALLOW_POSTDATE)) { + if (!tgt.getFlag(TicketFlags.MAY_POSTDATE)) + throw KerberosException.KDC_ERR_BADOPTION; + newTicket.setFlag(TicketFlags.MAY_POSTDATE); + } + + if (request.getOption(KdcOptions.POSTDATED)) { + if (!tgt.getFlag(TicketFlags.MAY_POSTDATE)) + throw KerberosException.KDC_ERR_BADOPTION; + newTicket.setFlag(TicketFlags.POSTDATED); + newTicket.setFlag(TicketFlags.INVALID); + + if (!LocalConfig.KDC_POSTDATE_ALLOWED) + throw KerberosException.KDC_ERR_POLICY; + + newTicket.setStartTime(request.getFrom()); + } + + if (request.getOption(KdcOptions.VALIDATE)) { + if (!tgt.getFlag(TicketFlags.INVALID)) + throw KerberosException.KDC_ERR_POLICY; + if (tgt.getStartTime().greaterThan(new KerberosTime())) + throw KerberosException.KRB_AP_ERR_TKT_NYV; + /* + if (check_hot_list(tgt)) then + error_out(KRB_AP_ERR_REPEAT); + endif + */ + + // TODO - tkt = tgt; + newTicket.clearFlag(TicketFlags.INVALID); + } + + if (request.getOption(KdcOptions.RESERVED) || + request.getOption(KdcOptions.RENEWABLE) || + request.getOption(KdcOptions.RENEWABLE_OK)) + throw KerberosException.KRB_AP_ERR_TKT_NYV; + } + + private void processTimes(KdcRequest request, Ticket newTicket, Ticket tgt) + throws KerberosException { + + KerberosTime now = new KerberosTime(); + + newTicket.setAuthtime(tgt.getAuthtime()); + + if (request.getOption(KdcOptions.RENEW)) { + /* + * Note that if the endtime has already passed, the ticket would have been + * rejected in the initial authentication stage, so there is no need to check again here + */ + if (!tgt.getFlag(TicketFlags.RENEWABLE)) + throw KerberosException.KDC_ERR_BADOPTION; + if (tgt.getRenewTill().greaterThan(now)) + throw KerberosException.KRB_AP_ERR_TKT_EXPIRED; + + newTicket = tgt; + + newTicket.setStartTime(now); + long oldLife = tgt.getEndTime().getTime() - tgt.getStartTime().getTime(); + newTicket.setEndTime(new KerberosTime(Math.min(tgt.getRenewTill().getTime(), newTicket.getStartTime().getTime() + oldLife))); + } else { + newTicket.setStartTime(now); + KerberosTime till; + if (request.getTill().isZero()) + till = KerberosTime.INFINITY; + else + till = request.getTill(); + + // TODO - config; requires store + /* + new_tkt.starttime+client.max_life, + new_tkt.starttime+server.max_life, + */ + List minimizer = new ArrayList(); + minimizer.add(till); + minimizer.add(new KerberosTime(newTicket.getStartTime().getTime() + LocalConfig.KDC_MAXIMUM_TICKET_LIFETIME)); + minimizer.add(tgt.getEndTime()); + newTicket.setEndTime((KerberosTime)Collections.min(minimizer)); + + if (request.getOption(KdcOptions.RENEWABLE_OK) && + newTicket.getEndTime().lessThan(request.getTill()) && + tgt.getFlag(TicketFlags.RENEWABLE)) { + // we set the RENEWABLE option for later processing + request.setOption(KdcOptions.RENEWABLE); + long rtime = Math.min(request.getTill().getTime(), tgt.getRenewTill().getTime()); + request.setRtime(new KerberosTime(rtime)); + } + } + + KerberosTime rtime; + if (request.getRtime() != null && request.getRtime().isZero()) + rtime = KerberosTime.INFINITY; + else + rtime = request.getRtime(); + + if (request.getOption(KdcOptions.RENEWABLE) && + tgt.getFlag(TicketFlags.RENEWABLE)) { + newTicket.setFlag(TicketFlags.RENEWABLE); + + /* + new_tkt.starttime+client.max_rlife, + new_tkt.starttime+server.max_rlife, + */ + // TODO - client and server configurable; requires store + List minimizer = new ArrayList(); + minimizer.add(rtime); + minimizer.add(new KerberosTime(newTicket.getStartTime().getTime() + LocalConfig.DEFAULT_MAXIMUM_RENEWABLE_LIFETIME)); + minimizer.add(tgt.getRenewTill()); + newTicket.setRenewTill((KerberosTime)Collections.min(minimizer)); + } + } + + private AuthorizationData processAuthorizationData(KdcRequest request, + Authenticator authHeader, Ticket tgt) throws KerberosException { + + AuthorizationData authData = null; + + if (request.getEncAuthorizationData() != null) { + try { + CryptoService enc = new CryptoService(); + byte[] decryptedAuthData = enc.decrypt(authHeader.getSubSessionKey(), + request.getEncAuthorizationData()); + AuthorizationDataDecoder decoder = new AuthorizationDataDecoder(); + authData = decoder.decode(decryptedAuthData); + } catch (KerberosException e) { + throw KerberosException.KRB_AP_ERR_BAD_INTEGRITY; + } catch (IOException ioe) { + throw KerberosException.KRB_AP_ERR_BAD_INTEGRITY; + } + + AuthorizationData ticketData = tgt.getAuthorizationData(); + authData.add(ticketData); + } + + return authData; + } + + /* + if (realm_tgt_is_for(tgt) := tgt.realm) then + // tgt issued by local realm + new_tkt.transited := tgt.transited; + else + // was issued for this realm by some other realm + if (tgt.transited.tr-type not supported) then + error_out(KDC_ERR_TRTYPE_NOSUPP); + endif + new_tkt.transited := compress_transited(tgt.transited + tgt.realm) + endif + */ + private void processTransited(Ticket newTicket, Ticket tgt) { + // TODO - currently no transited support other than local + newTicket.setTransitedEncoding(tgt.getTransitedEncoding()); + } + + // TODO - support multiple encryption types, this is hardwired for DES_CBC_MD5 + private void encryptTicketPart(Ticket newTicket, EncryptionKey serverKey, KdcRequest request) + throws KerberosException { + + byte[] encodedTicket; + + EncTicketPartEncoder encoder = new EncTicketPartEncoder(); + try { + encodedTicket = encoder.encode(newTicket); + } catch (IOException ioe) { + // TODO - figure out right error for ASN.1 generation error + throw KerberosException.KRB_ERR_GENERIC; + } + + CryptoService enc = new CryptoService(); + + if (request.getOption(KdcOptions.ENC_TKT_IN_SKEY)) { + /* + if (server not specified) then + server = req.second_ticket.client; + endif + if ((req.second_ticket is not a TGT) or + (req.second_ticket.client != server)) then + error_out(KDC_ERR_POLICY); + endif + new_tkt.enc-part := encrypt OCTET STRING + using etype_for_key(second-ticket.key), second-ticket.key; + */ + } else { + + EncryptedData cipherText = enc.getEncryptedData(serverKey, encodedTicket); + + newTicket.setEncPart(cipherText); + } + } + + // TODO - support multiple encryption types, this is hardwired for DES_CBC_MD5 + private void encryptReplyPart(TicketGrantReply reply, EncryptionKey key) { + EncTgsRepPartEncoder encoder = new EncTgsRepPartEncoder(); + try { + byte[] plainText = encoder.encode(reply); + + CryptoService enc = new CryptoService(); + + EncryptedData cipherText = enc.getEncryptedData(key, plainText); + + reply.setEncPart(cipherText); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + private TicketGrantReply getReply(Ticket tgt, Ticket newTicket, + EncryptionKey sessionKey, KdcRequest request) { + + TicketGrantReply reply = new TicketGrantReply(); + reply.setCrealm(tgt.getClientRealm()); + reply.setCname(tgt.getClientName()); + reply.setTicket(newTicket); + reply.setKey(sessionKey); + reply.setNonce(request.getNonce()); + // TODO - resp.last-req := fetch_last_request_info(client); requires store + reply.setLastRequest(new LastRequest()); + reply.setFlags(newTicket.getFlags()); + reply.setClientAddresses(newTicket.getClientAddresses()); + reply.setAuthTime(newTicket.getAuthtime()); + reply.setStartTime(newTicket.getStartTime()); + reply.setEndTime(newTicket.getEndTime()); + reply.setServerName(newTicket.getServerName()); + reply.setServerRealm(newTicket.getRealm()); + + if (newTicket.getFlag(TicketFlags.RENEWABLE)) + reply.setRenewTill(newTicket.getRenewTill()); + + return reply; + } +} +
