1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.kerberos.changepwd.service;
21
22 import java.net.InetAddress;
23 import java.nio.ByteBuffer;
24
25 import javax.security.auth.kerberos.KerberosPrincipal;
26
27 import org.apache.directory.api.asn1.ber.Asn1Decoder;
28 import org.apache.directory.api.util.Network;
29 import org.apache.directory.api.util.Strings;
30 import org.apache.directory.server.i18n.I18n;
31 import org.apache.directory.server.kerberos.ChangePasswordConfig;
32 import org.apache.directory.server.kerberos.changepwd.exceptions.ChangePasswdErrorType;
33 import org.apache.directory.server.kerberos.changepwd.exceptions.ChangePasswordException;
34 import org.apache.directory.server.kerberos.changepwd.messages.AbstractPasswordMessage;
35 import org.apache.directory.server.kerberos.changepwd.messages.ChangePasswordReply;
36 import org.apache.directory.server.kerberos.changepwd.messages.ChangePasswordRequest;
37 import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
38 import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
39 import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
40 import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
41 import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
42 import org.apache.directory.shared.kerberos.KerberosUtils;
43 import org.apache.directory.shared.kerberos.codec.KerberosDecoder;
44 import org.apache.directory.shared.kerberos.codec.changePwdData.ChangePasswdDataContainer;
45 import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
46 import org.apache.directory.shared.kerberos.codec.types.PrincipalNameType;
47 import org.apache.directory.shared.kerberos.components.EncKrbPrivPart;
48 import org.apache.directory.shared.kerberos.components.EncryptedData;
49 import org.apache.directory.shared.kerberos.components.EncryptionKey;
50 import org.apache.directory.shared.kerberos.components.HostAddress;
51 import org.apache.directory.shared.kerberos.components.HostAddresses;
52 import org.apache.directory.shared.kerberos.components.PrincipalName;
53 import org.apache.directory.shared.kerberos.exceptions.ErrorType;
54 import org.apache.directory.shared.kerberos.exceptions.KerberosException;
55 import org.apache.directory.shared.kerberos.messages.ApRep;
56 import org.apache.directory.shared.kerberos.messages.ApReq;
57 import org.apache.directory.shared.kerberos.messages.Authenticator;
58 import org.apache.directory.shared.kerberos.messages.ChangePasswdData;
59 import org.apache.directory.shared.kerberos.messages.EncApRepPart;
60 import org.apache.directory.shared.kerberos.messages.KrbPriv;
61 import org.apache.directory.shared.kerberos.messages.Ticket;
62 import org.apache.mina.core.session.IoSession;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66 public final class ChangePasswordService
67 {
68
69 private static final Logger LOG = LoggerFactory.getLogger( ChangePasswordService.class );
70
71 private static final CipherTextHandler/crypto/encryption/CipherTextHandler.html#CipherTextHandler">CipherTextHandler CIPHER_TEXT_HANDLER = new CipherTextHandler();
72
73
74 private ChangePasswordService()
75 {
76 }
77
78
79 public static void execute( IoSession session, ChangePasswordContext changepwContext ) throws Exception
80 {
81 if ( LOG.isDebugEnabled() )
82 {
83 monitorRequest( changepwContext );
84 }
85
86 configureChangePassword( changepwContext );
87 getAuthHeader( changepwContext );
88 verifyServiceTicket( changepwContext );
89 getServerEntry( changepwContext );
90 verifyServiceTicketAuthHeader( changepwContext );
91 extractPassword( changepwContext );
92
93 if ( LOG.isDebugEnabled() )
94 {
95 monitorContext( changepwContext );
96 }
97
98 processPasswordChange( changepwContext );
99 buildReply( changepwContext );
100
101 if ( LOG.isDebugEnabled() )
102 {
103 monitorReply( changepwContext );
104 }
105 }
106
107
108 private static void processPasswordChange( ChangePasswordContext changepwContext ) throws KerberosException
109 {
110 PrincipalStore store = changepwContext.getStore();
111 Authenticator authenticator = changepwContext.getAuthenticator();
112 String newPassword = Strings.utf8ToString( changepwContext.getPasswordData().getNewPasswd() );
113 KerberosPrincipal byPrincipal = KerberosUtils.getKerberosPrincipal(
114 authenticator.getCName(),
115 authenticator.getCRealm() );
116
117 KerberosPrincipal targetPrincipal = null;
118
119 PrincipalName targName = changepwContext.getPasswordData().getTargName();
120
121 if ( targName != null )
122 {
123 targetPrincipal = new KerberosPrincipal( targName.getNameString(), PrincipalNameType.KRB_NT_PRINCIPAL.getValue() );
124 }
125 else
126 {
127 targetPrincipal = byPrincipal;
128 }
129
130
131
132
133
134 store.changePassword( byPrincipal, targetPrincipal, newPassword, changepwContext.getTicket().getEncTicketPart().getFlags().isInitial() );
135 LOG.debug( "Successfully modified password for {} BY {}.", targetPrincipal, byPrincipal );
136 }
137
138
139 private static void monitorRequest( ChangePasswordContext changepwContext )
140 {
141 try
142 {
143 ChangePasswordRequest/../org/apache/directory/server/kerberos/changepwd/messages/ChangePasswordRequest.html#ChangePasswordRequest">ChangePasswordRequest request = ( ChangePasswordRequest ) changepwContext.getRequest();
144 short versionNumber = request.getVersionNumber();
145
146 if ( LOG.isDebugEnabled() )
147 {
148 LOG.debug( "Responding to change password request:\\n\\tversionNumber {}", versionNumber );
149 }
150 }
151 catch ( Exception e )
152 {
153
154 LOG.error( I18n.err( I18n.ERR_152 ), e );
155 }
156 }
157
158
159 private static void configureChangePassword( ChangePasswordContext changepwContext )
160 {
161 changepwContext.setCipherTextHandler( CIPHER_TEXT_HANDLER );
162 }
163
164
165 private static void getAuthHeader( ChangePasswordContext changepwContext ) throws KerberosException
166 {
167 ChangePasswordRequest/../org/apache/directory/server/kerberos/changepwd/messages/ChangePasswordRequest.html#ChangePasswordRequest">ChangePasswordRequest request = ( ChangePasswordRequest ) changepwContext.getRequest();
168
169 short pvno = request.getVersionNumber();
170
171 if ( ( pvno != AbstractPasswordMessage.PVNO ) && ( pvno != AbstractPasswordMessage.OLD_PVNO ) )
172 {
173 throw new ChangePasswordException( ChangePasswdErrorType.KRB5_KPASSWD_BAD_VERSION );
174 }
175
176 if ( request.getAuthHeader() == null || request.getAuthHeader().getTicket() == null )
177 {
178 throw new ChangePasswordException( ChangePasswdErrorType.KRB5_KPASSWD_AUTHERROR );
179 }
180
181 ApReq authHeader = request.getAuthHeader();
182 Ticket ticket = authHeader.getTicket();
183
184 changepwContext.setAuthHeader( authHeader );
185 changepwContext.setTicket( ticket );
186 }
187
188
189 private static void verifyServiceTicket( ChangePasswordContext changepwContext ) throws KerberosException
190 {
191 ChangePasswordConfig config = changepwContext.getConfig();
192 Ticket ticket = changepwContext.getTicket();
193 String primaryRealm = config.getPrimaryRealm();
194 KerberosPrincipal changepwPrincipal = config.getServicePrincipal();
195 KerberosPrincipal serverPrincipal = KerberosUtils.getKerberosPrincipal( ticket.getSName(), ticket.getRealm() );
196
197
198
199
200 if ( !ticket.getRealm().equals( primaryRealm ) || !serverPrincipal.getName().equals( changepwPrincipal.getName() ) )
201 {
202 throw new KerberosException( org.apache.directory.shared.kerberos.exceptions.ErrorType.KRB_AP_ERR_NOT_US );
203 }
204 }
205
206
207 private static void getServerEntry( ChangePasswordContext changepwContext ) throws KerberosException
208 {
209 Ticket ticket = changepwContext.getTicket();
210 KerberosPrincipal principal = KerberosUtils.getKerberosPrincipal( ticket.getSName(), ticket.getRealm() );
211 PrincipalStore store = changepwContext.getStore();
212
213 changepwContext.setServerEntry( KerberosUtils.getEntry( principal, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN ) );
214 }
215
216
217 private static void verifyServiceTicketAuthHeader( ChangePasswordContext changepwContext ) throws KerberosException
218 {
219 ApReq authHeader = changepwContext.getAuthHeader();
220 Ticket ticket = changepwContext.getTicket();
221
222 EncryptionType encryptionType = ticket.getEncPart().getEType();
223 EncryptionKey serverKey = changepwContext.getServerEntry().getKeyMap().get( encryptionType );
224
225 long clockSkew = changepwContext.getConfig().getAllowableClockSkew();
226 ReplayCache replayCache = changepwContext.getReplayCache();
227 boolean emptyAddressesAllowed = changepwContext.getConfig().isEmptyAddressesAllowed();
228 InetAddress clientAddress = changepwContext.getClientAddress();
229 CipherTextHandler cipherTextHandler = changepwContext.getCipherTextHandler();
230
231 Authenticator authenticator = KerberosUtils.verifyAuthHeader( authHeader, ticket, serverKey, clockSkew, replayCache,
232 emptyAddressesAllowed, clientAddress, cipherTextHandler, KeyUsage.AP_REQ_AUTHNT_SESS_KEY, false );
233
234 changepwContext.setAuthenticator( authenticator );
235 }
236
237
238 private static void extractPassword( ChangePasswordContext changepwContext ) throws Exception
239 {
240 ChangePasswordRequest/../org/apache/directory/server/kerberos/changepwd/messages/ChangePasswordRequest.html#ChangePasswordRequest">ChangePasswordRequest request = ( ChangePasswordRequest ) changepwContext.getRequest();
241 Authenticator authenticator = changepwContext.getAuthenticator();
242 CipherTextHandler cipherTextHandler = changepwContext.getCipherTextHandler();
243
244
245 EncryptionKey subSessionKey = authenticator.getSubKey();
246
247
248 EncryptedData encReqPrivPart = request.getPrivateMessage().getEncPart();
249
250 ChangePasswdData passwordData = null;
251
252 try
253 {
254 byte[] decryptedData = cipherTextHandler.decrypt( subSessionKey, encReqPrivPart, KeyUsage.KRB_PRIV_ENC_PART_CHOSEN_KEY );
255 EncKrbPrivPart privatePart = KerberosDecoder.decodeEncKrbPrivPart( decryptedData );
256
257 if ( ( authenticator.getSeqNumber() != null ) && ( authenticator.getSeqNumber() != privatePart.getSeqNumber() ) )
258 {
259 throw new ChangePasswordException( ChangePasswdErrorType.KRB5_KPASSWD_MALFORMED );
260 }
261
262 if ( request.getVersionNumber() == AbstractPasswordMessage.OLD_PVNO )
263 {
264 passwordData = new ChangePasswdData();
265 passwordData.setNewPasswd( privatePart.getUserData() );
266 }
267 else
268 {
269 ByteBuffer stream = ByteBuffer.wrap( privatePart.getUserData() );
270 ChangePasswdDataContainerc/changePwdData/ChangePasswdDataContainer.html#ChangePasswdDataContainer">ChangePasswdDataContainer container = new ChangePasswdDataContainer( stream );
271 Asn1Decoder.decode( stream, container );
272 passwordData = container.getChngPwdData();
273 }
274 }
275 catch ( KerberosException ke )
276 {
277 throw new ChangePasswordException( ChangePasswdErrorType.KRB5_KPASSWD_SOFTERROR, ke );
278 }
279
280 changepwContext.setChngPwdData( passwordData );
281 }
282
283
284 private static void monitorContext( ChangePasswordContext changepwContext )
285 {
286 try
287 {
288 PrincipalStore store = changepwContext.getStore();
289 ApReq authHeader = changepwContext.getAuthHeader();
290 Ticket ticket = changepwContext.getTicket();
291 ReplayCache replayCache = changepwContext.getReplayCache();
292 long clockSkew = changepwContext.getConfig().getAllowableClockSkew();
293
294 Authenticator authenticator = changepwContext.getAuthenticator();
295 KerberosPrincipal clientPrincipal = KerberosUtils.getKerberosPrincipal(
296 authenticator.getCName(), authenticator.getCRealm() );
297
298 InetAddress clientAddress = changepwContext.getClientAddress();
299 HostAddresses clientAddresses = ticket.getEncTicketPart().getClientAddresses();
300
301 boolean caddrContainsSender = false;
302
303 if ( ticket.getEncTicketPart().getClientAddresses() != null )
304 {
305 caddrContainsSender = ticket.getEncTicketPart().getClientAddresses().contains( new HostAddress( clientAddress ) );
306 }
307
308 if ( LOG.isDebugEnabled() )
309 {
310 StringBuilder sb = new StringBuilder();
311 sb.append( "Monitoring context:" );
312 sb.append( "\n\tstore " ).append( store );
313 sb.append( "\n\tauthHeader " ).append( authHeader );
314 sb.append( "\n\tticket " ).append( ticket );
315 sb.append( "\n\treplayCache " ).append( replayCache );
316 sb.append( "\n\tclockSkew " ).append( clockSkew );
317 sb.append( "\n\tclientPrincipal " ).append( clientPrincipal );
318 sb.append( "\n\tChangePasswdData " ).append( changepwContext.getPasswordData() );
319 sb.append( "\n\tclientAddress " ).append( clientAddress );
320 sb.append( "\n\tclientAddresses " ).append( clientAddresses );
321 sb.append( "\n\tcaddr contains sender " ).append( caddrContainsSender );
322 sb.append( "\n\tTicket principal " ).append( ticket.getSName() );
323
324 PrincipalStoreEntry ticketPrincipal = changepwContext.getServerEntry();
325
326 sb.append( "\n\tcn " ).append( ticketPrincipal.getCommonName() );
327 sb.append( "\n\trealm " ).append( ticketPrincipal.getRealmName() );
328 sb.append( "\n\tService principal " ).append( ticketPrincipal.getPrincipal() );
329 sb.append( "\n\tSAM type " ).append( ticketPrincipal.getSamType() );
330
331 EncryptionType encryptionType = ticket.getEncPart().getEType();
332 int keyVersion = ticketPrincipal.getKeyMap().get( encryptionType ).getKeyVersion();
333 sb.append( "\n\tTicket key type " ).append( encryptionType );
334 sb.append( "\n\tService key version " ).append( keyVersion );
335
336 LOG.debug( sb.toString() );
337 }
338 }
339 catch ( Exception e )
340 {
341
342 LOG.error( I18n.err( I18n.ERR_154 ), e );
343 }
344 }
345
346
347 private static void buildReply( ChangePasswordContext changepwContext ) throws KerberosException
348 {
349 Authenticator authenticator = changepwContext.getAuthenticator();
350 Ticket ticket = changepwContext.getTicket();
351 CipherTextHandler cipherTextHandler = changepwContext.getCipherTextHandler();
352
353
354
355
356
357 EncKrbPrivParterberos/components/EncKrbPrivPart.html#EncKrbPrivPart">EncKrbPrivPart privPart = new EncKrbPrivPart();
358
359 byte[] resultCode =
360 { ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x50, ( byte ) 0x61, ( byte ) 0x73, ( byte ) 0x73, ( byte ) 0x77,
361 ( byte ) 0x6F, ( byte ) 0x72, ( byte ) 0x64, ( byte ) 0x20, ( byte ) 0x63, ( byte ) 0x68,
362 ( byte ) 0x61, ( byte ) 0x6E, ( byte ) 0x67, ( byte ) 0x65, ( byte ) 0x64, ( byte ) 0x00 };
363 privPart.setUserData( resultCode );
364
365 privPart.setSenderAddress( new HostAddress( Network.LOOPBACK ) );
366
367
368 EncryptionKey subSessionKey = authenticator.getSubKey();
369
370 EncryptedData encPrivPart;
371
372 try
373 {
374 encPrivPart = cipherTextHandler.seal( subSessionKey, privPart, KeyUsage.KRB_PRIV_ENC_PART_CHOSEN_KEY );
375 }
376 catch ( KerberosException ke )
377 {
378 throw new ChangePasswordException( ChangePasswdErrorType.KRB5_KPASSWD_SOFTERROR, ke );
379 }
380
381 KrbPrivkerberos/messages/KrbPriv.html#KrbPriv">KrbPriv privateMessage = new KrbPriv();
382 privateMessage.setEncPart( encPrivPart );
383
384
385 EncApRepPartd/kerberos/messages/EncApRepPart.html#EncApRepPart">EncApRepPart repPart = new EncApRepPart();
386 repPart.setCTime( authenticator.getCtime() );
387 repPart.setCusec( authenticator.getCusec() );
388
389 if ( authenticator.getSeqNumber() != null )
390 {
391 repPart.setSeqNumber( authenticator.getSeqNumber() );
392 }
393
394 repPart.setSubkey( subSessionKey );
395
396 EncryptedData encRepPart;
397
398 try
399 {
400 encRepPart = cipherTextHandler.seal( ticket.getEncTicketPart().getKey(), repPart, KeyUsage.AP_REP_ENC_PART_SESS_KEY );
401 }
402 catch ( KerberosException ke )
403 {
404 throw new ChangePasswordException( ChangePasswdErrorType.KRB5_KPASSWD_SOFTERROR, ke );
405 }
406
407 ApRep/shared/kerberos/messages/ApRep.html#ApRep">ApRep appReply = new ApRep();
408 appReply.setEncPart( encRepPart );
409
410
411 changepwContext.setReply( new ChangePasswordReply( AbstractPasswordMessage.OLD_PVNO, appReply, privateMessage ) );
412 }
413
414
415 private static void monitorReply( ChangePasswordContext changepwContext )
416 {
417 try
418 {
419 ChangePasswordReply./../../org/apache/directory/server/kerberos/changepwd/messages/ChangePasswordReply.html#ChangePasswordReply">ChangePasswordReply reply = ( ChangePasswordReply ) changepwContext.getReply();
420 ApRep appReply = reply.getApplicationReply();
421 KrbPriv priv = reply.getPrivateMessage();
422
423 LOG.debug( "Responding with change password reply:\\n\\tappReply {}\\n\\tpriv {}",
424 appReply, priv );
425 }
426 catch ( Exception e )
427 {
428
429 LOG.error( I18n.err( I18n.ERR_155 ), e );
430 }
431 }
432 }