You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by er...@apache.org on 2004/11/03 20:30:06 UTC
svn commit: rev 56516 - incubator/directory/kerberos/trunk/source/main/org/apache/kerberos/changepw
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;
+ }
+}
+