Author: erodriguez
Date: Wed Nov  3 11:30:05 2004
New Revision: 56516

Added:
   
incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/changepw/ChangePasswordDispatcher.java
   
incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/changepw/ChangePasswordService.java
Log:
Core of the change password service.  Tested with gnome-kerberos, Apache 
Kerberos server, and OpenLDAP.

Added: 
incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/changepw/ChangePasswordDispatcher.java
==============================================================================
--- (empty file)
+++ 
incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/changepw/ChangePasswordDispatcher.java
   Wed Nov  3 11:30:05 2004
@@ -0,0 +1,81 @@
+/*
+ *   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.changepw;
+
+import org.apache.kerberos.changepw.io.*;
+import org.apache.kerberos.changepw.messages.*;
+import org.apache.kerberos.changepw.store.*;
+import org.apache.kerberos.crypto.*;
+import org.apache.kerberos.kdc.*;
+import org.apache.kerberos.kdc.store.*;
+
+import java.io.*;
+
+public class ChangePasswordDispatcher {
+       
+       private PrincipalStore   _bootstrap;
+       private CryptoService    _cryptoService;
+       private KdcConfiguration _config;
+       private PasswordStore    _store;
+       
+       private ChangePasswordService      _changepwService;
+       private ChangePasswordErrorService _errorService;
+       
+       public ChangePasswordDispatcher(KdcConfiguration config, BootstrapStore 
bootstrap, PasswordStore store) {
+               
+               _config    = config;
+               _bootstrap = bootstrap;
+               _store     = store;
+               
+               _cryptoService   = new CryptoService(_config);
+               _changepwService = new ChangePasswordService(_store, 
_bootstrap, _cryptoService, _config);
+       }
+       
+       public byte[] dispatch(byte[] requestBytes) throws IOException {
+               
+               byte[] reply = null;
+               
+               try {
+                       ChangePasswordRequestDecoder decoder = new 
ChangePasswordRequestDecoder();
+                       ChangePasswordRequest changepwRequest = 
decoder.decode(requestBytes);
+                       
+                       ChangePasswordReply changepwReply = 
_changepwService.getReplyFor(changepwRequest);
+                       
+                       ChangePasswordReplyEncoder encoder = new 
ChangePasswordReplyEncoder();
+                       reply = encoder.encode(changepwReply);
+                       
+               } catch (KerberosException ke) {
+                       
+                       System.out.println("Returning error message:  " + 
ke.getMessage());
+                       ChangePasswordError errorMessage = 
_errorService.getReplyFor(ke);
+                       ChangePasswordErrorEncoder errorEncoder = new 
ChangePasswordErrorEncoder();
+                       reply = errorEncoder.encode(errorMessage);
+                       
+               } catch (IOException ioe) {
+                       
+                       System.out.println("Returning error message:  " + 
ioe.getMessage());
+                       ioe.printStackTrace();
+                       ChangePasswordError errorMessage =
+                               
_errorService.getReplyFor(ChangePasswordException.KRB5_KPASSWD_MALFORMED);
+                       ChangePasswordErrorEncoder errorEncoder = new 
ChangePasswordErrorEncoder();
+                       reply = errorEncoder.encode(errorMessage);
+               }
+               
+               return reply;
+       }
+}
+

Added: 
incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/changepw/ChangePasswordService.java
==============================================================================
--- (empty file)
+++ 
incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/changepw/ChangePasswordService.java
      Wed Nov  3 11:30:05 2004
@@ -0,0 +1,271 @@
+/*
+ *   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.changepw;
+
+import org.apache.kerberos.changepw.io.*;
+import org.apache.kerberos.changepw.messages.*;
+import org.apache.kerberos.changepw.store.*;
+import org.apache.kerberos.changepw.value.*;
+import org.apache.kerberos.crypto.*;
+import org.apache.kerberos.io.decoder.*;
+import org.apache.kerberos.io.encoder.*;
+import org.apache.kerberos.kdc.*;
+import org.apache.kerberos.kdc.store.*;
+import org.apache.kerberos.messages.*;
+import org.apache.kerberos.messages.application.*;
+import org.apache.kerberos.messages.components.*;
+import org.apache.kerberos.messages.components.Authenticator;
+import org.apache.kerberos.messages.value.*;
+
+import java.io.*;
+import java.net.*;
+
+import javax.security.auth.kerberos.*;
+
+/**
+ * Kerberos Change Password and Set Password Protocols (RFC 3244)
+ */
+public class ChangePasswordService {
+       
+       private PasswordStore    _store;
+       private PrincipalStore   _bootstrap;
+       private CryptoService    _cryptoService;
+       private KdcConfiguration _config;
+       
+       public ChangePasswordService(PasswordStore store, PrincipalStore 
bootstrap,
+                       CryptoService cryptoService, KdcConfiguration config) {
+               
+               _store         = store;
+               _bootstrap     = bootstrap;
+               _cryptoService = cryptoService;
+               _config        = config;
+       }
+       
+       public ChangePasswordReply getReplyFor(ChangePasswordRequest request)
+                       throws KerberosException, IOException {
+               
+               ApplicationRequest authHeader = request.getAuthHeader();
+               
+               Ticket ticket = authHeader.getTicket();
+               
+               Authenticator authenticator = verifyAuthHeader(authHeader, 
ticket);
+               
+               verifyTicket(ticket, _config.getChangepwPrincipal());
+               
+               // TODO - check ticket is for service authorized to change 
passwords
+               // 
ticket.getServerPrincipal().getName().equals(_config.getChangepwPrincipal().getName()));
+               
+               // TODO - check client principal in ticket is authorized to 
change password
+               
+               // get the subsession key from the Authenticator
+               EncryptionKey sessionKey = authenticator.getSubSessionKey();
+               
+               // decrypt the request's private message with the subsession key
+               EncryptedData encReqPrivPart = 
request.getPrivateMessage().getEncryptedPart();
+               EncKrbPrivPart privatePart;
+               try {
+                       byte[] decPrivPart = _cryptoService.decrypt(sessionKey, 
encReqPrivPart);
+
+                       EncKrbPrivPartDecoder privDecoder = new 
EncKrbPrivPartDecoder();
+                       privatePart = privDecoder.decode(decPrivPart);
+               } catch (KerberosException ke) {
+                       ke.printStackTrace();
+                       throw ChangePasswordException.KRB5_KPASSWD_AUTHERROR;
+               }
+               
+               ChangePasswordData passwordData = null;
+               
+               if (request.getProtocolVersionNumber() == (short)1) {
+                       // Use protocol version 0x0001, the legacy Kerberos 
change password protocol
+                       ChangePasswordDataModifier modifier = new 
ChangePasswordDataModifier();
+                       modifier.setNewPassword(privatePart.getUserData());
+                       passwordData = modifier.getChangePasswdData();
+               } else {
+                       // Use protocol version 0xFF80, the 
backwards-compatible MS protocol
+                       ChangePasswordDataDecoder passwordDecoder = new 
ChangePasswordDataDecoder();
+                       passwordData = 
passwordDecoder.decodeChangePasswordData(privatePart.getUserData());
+               }
+               
+               // usec and seq-number must be present per MS but aren't in 
legacy kpasswd
+               // seq-number must have same value as authenticator
+               // ignore r-address
+               
+               // generate key from password
+               String password = new String(passwordData.getNewPassword());
+               KerberosPrincipal clientPrincipal = 
authenticator.getClientPrincipal();
+               KerberosKey newKey = new KerberosKey(clientPrincipal, 
password.toCharArray(), "DES");
+               
+               // store password in database
+               String principalName = _store.changePassword(clientPrincipal, 
newKey.getEncoded());
+               System.out.println("Successfully modified principal named " + 
principalName);
+               
+               // begin building reply
+               
+               // create priv message
+               // user-data component is short result code
+               EncKrbPrivPartModifier modifier = new EncKrbPrivPartModifier();
+               byte[] resultCode = {(byte)0x00, (byte)0x00};
+               modifier.setUserData(resultCode);
+               
+               modifier.setSenderAddress(new 
HostAddress(InetAddress.getLocalHost()));
+               EncKrbPrivPart privPart = modifier.getEncKrbPrivPart();
+               
+               EncKrbPrivPartEncoder encoder = new EncKrbPrivPartEncoder();
+               byte[] encodedPrivPart = encoder.encode(privPart);
+               
+               EncryptedData encPrivPart = null;
+               try {
+                       encPrivPart = 
_cryptoService.getEncryptedData(sessionKey, encodedPrivPart);
+               } catch (KerberosException ke) {
+                       ke.printStackTrace();
+               }
+               PrivateMessage privateMessage = new PrivateMessage(encPrivPart);
+               
+               // Begin AP_REP generation
+               EncApRepPartModifier encApModifier = new EncApRepPartModifier();
+               encApModifier.setClientTime(authenticator.getClientTime());
+               
encApModifier.setClientMicroSecond(authenticator.getClientMicroSecond());
+               encApModifier.setSequenceNumber(new 
Integer(authenticator.getSequenceNumber()));
+               
encApModifier.setSubSessionKey(authenticator.getSubSessionKey());
+               
+               EncApRepPart repPart = encApModifier.getEncApRepPart();
+               EncApRepPartEncoder repEncoder = new EncApRepPartEncoder();
+               byte[] encodedRepPart = repEncoder.encode(repPart);
+               
+               EncryptedData encRepPart = null;
+               try {
+                       encRepPart = 
_cryptoService.getEncryptedData(ticket.getSessionKey(), encodedRepPart);
+               } catch (KerberosException ke) {
+                       ke.printStackTrace();
+               }
+               ApplicationReply appReply = new ApplicationReply(encRepPart);
+               
+               // return status message value object
+               ChangePasswordReplyModifier replyModifier = new 
ChangePasswordReplyModifier();
+               replyModifier.setApplicationReply(appReply);
+               replyModifier.setPrivateMessage(privateMessage);
+               
+               return replyModifier.getChangePasswordReply();
+               
+       }
+       
+       // TODO - this is a duplicate from the TGS service, with the 
ReplayCache disabled and ...
+       // TODO - ... changepw doesn't have the same LDAP store access
+       // RFC 1510 A.10.  KRB_AP_REQ verification
+       private Authenticator verifyAuthHeader(ApplicationRequest authHeader, 
Ticket ticket)
+                       throws KerberosException, IOException {
+               
+               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 {
+                       KerberosPrincipal serverPrincipal = 
ticket.getServerPrincipal();
+                       PrincipalStoreEntry serverEntry = 
_bootstrap.getEntry(serverPrincipal);
+                       
+                       if (serverEntry != null) {
+                               serverKey = serverEntry.getEncryptionKey();
+                       }/*
+                        else {
+                               serverKey = 
_store.getEntry(serverPrincipal).getEncryptionKey();
+                       }
+                       */
+               }
+               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;
+               }
+               
+               try {
+                       byte[] decTicketPart = 
_cryptoService.decrypt(serverKey, ticket.getEncPart());
+
+                       EncTicketPartDecoder ticketPartDecoder = new 
EncTicketPartDecoder();
+                       EncTicketPart encPart = 
ticketPartDecoder.decode(decTicketPart);
+                       ticket.setEncTicketPart(encPart);
+               } catch (KerberosException ke) {
+                       throw KerberosException.KRB_AP_ERR_BAD_INTEGRITY;
+               }
+               
+               Authenticator authenticator;
+               
+               try {
+                       byte[] decAuthenticator = 
_cryptoService.decrypt(ticket.getSessionKey(), authHeader.getEncPart());
+                       AuthenticatorDecoder authDecoder = new 
AuthenticatorDecoder();
+                       authenticator = authDecoder.decode(decAuthenticator);
+               } catch (KerberosException ke) {
+                       throw KerberosException.KRB_AP_ERR_BAD_INTEGRITY;
+               }
+               
+               if 
(!authenticator.getClientPrincipal().getName().equals(ticket.getClientPrincipal().getName()))
 {
+                       throw KerberosException.KRB_AP_ERR_BADMATCH;
+               }
+               
+               // TODO - need to get at IP Address for sender
+               if (ticket.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.getClientPrincipal())) {
+                       throw KerberosException.KRB_AP_ERR_REPEAT;
+               }
+        
+               _replayCache.save(authenticator.getClientTime(), 
authenticator.getClientPrincipal());
+               */
+               
+               if 
(!authenticator.getClientTime().isInClockSkew(_config.getClockSkew()))
+                       throw KerberosException.KRB_AP_ERR_SKEW;
+               
+               if (ticket.getStartTime() != null && 
!ticket.getStartTime().isInClockSkew(_config.getClockSkew()) ||
+                               ticket.getFlag(TicketFlags.INVALID))
+                               // it hasn't yet become valid
+                throw KerberosException.KRB_AP_ERR_TKT_NYV;
+               
+               // TODO - doesn't take into account skew
+               if (!ticket.getEndTime().greaterThan(new KerberosTime()))
+            throw KerberosException.KRB_AP_ERR_TKT_EXPIRED;
+               
+               authHeader.setOption(ApOptions.MUTUAL_REQUIRED);
+               
+               return authenticator;
+       }
+       
+       // TODO - this is a duplicate from the TGS service
+       private void verifyTicket(Ticket ticket, KerberosPrincipal 
serverPrincipal)
+                       throws KerberosException {
+
+               if (!ticket.getRealm().equals(_config.getPrimaryRealm())
+                               && 
!ticket.getServerPrincipal().equals(serverPrincipal))
+                       throw KerberosException.KRB_AP_ERR_NOT_US;
+       }
+}
+

Reply via email to