001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *  
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *  
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License. 
018 *  
019 */
020package org.apache.directory.kerberos.client;
021
022
023import java.io.IOException;
024import java.nio.ByteBuffer;
025import java.security.SecureRandom;
026import java.text.ParseException;
027import java.util.List;
028
029import javax.security.auth.kerberos.KerberosPrincipal;
030
031import org.apache.directory.api.asn1.Asn1Object;
032import org.apache.directory.api.util.Network;
033import org.apache.directory.api.util.Strings;
034import org.apache.directory.server.kerberos.changepwd.exceptions.ChangePasswdErrorType;
035import org.apache.directory.server.kerberos.changepwd.exceptions.ChangePasswordException;
036import org.apache.directory.server.kerberos.changepwd.io.ChangePasswordDecoder;
037import org.apache.directory.server.kerberos.changepwd.io.ChangePasswordEncoder;
038import org.apache.directory.server.kerberos.changepwd.messages.AbstractPasswordMessage;
039import org.apache.directory.server.kerberos.changepwd.messages.ChangePasswordError;
040import org.apache.directory.server.kerberos.changepwd.messages.ChangePasswordReply;
041import org.apache.directory.server.kerberos.changepwd.messages.ChangePasswordRequest;
042import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
043import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
044import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
045import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
046import org.apache.directory.shared.kerberos.KerberosTime;
047import org.apache.directory.shared.kerberos.codec.KerberosDecoder;
048import org.apache.directory.shared.kerberos.codec.KerberosEncoder;
049import org.apache.directory.shared.kerberos.codec.KerberosMessageContainer;
050import org.apache.directory.shared.kerberos.codec.options.ApOptions;
051import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
052import org.apache.directory.shared.kerberos.codec.types.PaDataType;
053import org.apache.directory.shared.kerberos.codec.types.PrincipalNameType;
054import org.apache.directory.shared.kerberos.components.EncKdcRepPart;
055import org.apache.directory.shared.kerberos.components.EncKrbPrivPart;
056import org.apache.directory.shared.kerberos.components.EncryptedData;
057import org.apache.directory.shared.kerberos.components.EncryptionKey;
058import org.apache.directory.shared.kerberos.components.HostAddress;
059import org.apache.directory.shared.kerberos.components.HostAddresses;
060import org.apache.directory.shared.kerberos.components.KdcReqBody;
061import org.apache.directory.shared.kerberos.components.PaData;
062import org.apache.directory.shared.kerberos.components.PaEncTsEnc;
063import org.apache.directory.shared.kerberos.components.PrincipalName;
064import org.apache.directory.shared.kerberos.exceptions.ErrorType;
065import org.apache.directory.shared.kerberos.exceptions.KerberosException;
066import org.apache.directory.shared.kerberos.messages.ApReq;
067import org.apache.directory.shared.kerberos.messages.AsRep;
068import org.apache.directory.shared.kerberos.messages.AsReq;
069import org.apache.directory.shared.kerberos.messages.Authenticator;
070import org.apache.directory.shared.kerberos.messages.ChangePasswdData;
071import org.apache.directory.shared.kerberos.messages.EncAsRepPart;
072import org.apache.directory.shared.kerberos.messages.EncTgsRepPart;
073import org.apache.directory.shared.kerberos.messages.KerberosMessage;
074import org.apache.directory.shared.kerberos.messages.KrbError;
075import org.apache.directory.shared.kerberos.messages.KrbPriv;
076import org.apache.directory.shared.kerberos.messages.TgsRep;
077import org.apache.directory.shared.kerberos.messages.TgsReq;
078import org.slf4j.Logger;
079import org.slf4j.LoggerFactory;
080
081
082/**
083 * 
084 * A client to connect to kerberos servers using TCP or UDP transports.
085 * 
086 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
087 */
088public class KdcConnection
089{
090
091    private static final Logger LOG = LoggerFactory.getLogger( KdcConnection.class );
092
093    /** a secure random number generator used for creating nonces */
094    private SecureRandom nonceGenerator;
095
096    static final String TIME_OUT_ERROR = "TimeOut occured";
097
098    /** the cipher text handler */
099    private CipherTextHandler cipherTextHandler;
100
101    /** underlying network channel handler */
102    private KerberosChannel channel;
103
104    private KdcConfig config;
105
106
107    /**
108     * 
109     * Creates a new instance of KdcConnection.
110     *
111     * @param config the configuration of KDC
112     */
113    public KdcConnection( KdcConfig config )
114    {
115        this.config = config;
116
117        nonceGenerator = new SecureRandom(
118            Strings.getBytesUtf8( String.valueOf( System.currentTimeMillis() ) ) );
119        cipherTextHandler = new CipherTextHandler();
120        channel = new KerberosChannel();
121    }
122
123
124    private void connect() throws IOException
125    {
126        channel.openConnection( config.getHostName(), config.getKdcPort(), config.getTimeout(), config.isUseUdp() );
127    }
128
129
130    /**
131     * Authenticates to the Kerberos server and gets the initial Ticket Granting Ticket
132     * 
133     * @param principal the client's principal 
134     * @param password password of the client
135     * @return A Ticket Granting Ticket instance
136     * @throws KerberosException If a Ticket Granting Ticket cannot be fetch
137     */
138    public TgTicket getTgt( String principal, String password ) throws KerberosException
139    {
140        TgtRequest clientTgtReq = new TgtRequest();
141
142        clientTgtReq.setClientPrincipal( principal );
143        clientTgtReq.setPassword( password );
144
145        return getTgt( clientTgtReq );
146    }
147
148
149    /**
150     * Authenticates to the Kerberos server and gets a service ticket for the given server principal
151     * 
152     * @param clientPrincipal the client's principal 
153     * @param password password of the client
154     * @param serverPrincipal the application server's principal
155     * @return A ServiceTicket instance
156     * @throws KerberosException If the ServiceTicket cannot be fetch
157     */
158    public ServiceTicket getServiceTicket( String clientPrincipal, String password, String serverPrincipal )
159        throws KerberosException
160    {
161        TgtRequest clientTgtReq = new TgtRequest();
162        clientTgtReq.setClientPrincipal( clientPrincipal );
163        clientTgtReq.setPassword( password );
164
165        TgTicket tgt = getTgt( clientTgtReq );
166
167        return getServiceTicket( new ServiceTicketRequest( tgt, serverPrincipal ) );
168    }
169
170
171    public TgTicket getTgt( TgtRequest clientTgtReq ) throws KerberosException
172    {
173        TgTicket tgt = null;
174
175        KerberosException ke = null;
176
177        for ( int i = 0; i < 2; i++ )
178        {
179            ke = null;
180
181            try
182            {
183                tgt = _getTgt( clientTgtReq );
184            }
185            catch ( KerberosException e )
186            {
187                // using exception for control flow, b.a.d, but here it is better than
188                // defining a new Result class to hold ticket and exception and validating
189                // the Result instance from _getTgt()
190                ke = e;
191            }
192
193            if ( ( ke != null ) && ( ke.getErrorCode() == ErrorType.KDC_ERR_PREAUTH_REQUIRED.getValue() ) )
194            {
195                clientTgtReq.setETypes( KdcClientUtil.getEtypesFromError( ke.getError() ) );
196                clientTgtReq.setPreAuthEnabled( true );
197            }
198        }
199
200        if ( ke != null )
201        {
202            throw ke;
203        }
204
205        return tgt;
206    }
207
208
209    /* default protected */ TgTicket _getTgt( TgtRequest clientTgtReq ) throws KerberosException
210    {
211        String realm = clientTgtReq.getRealm();
212
213        if ( clientTgtReq.getServerPrincipal() == null )
214        {
215            String serverPrincipal = "krbtgt/" + realm + "@" + realm;
216            clientTgtReq.setServerPrincipal( serverPrincipal );
217        }
218
219        if ( clientTgtReq.getETypes() == null )
220        {
221            clientTgtReq.setETypes( config.getEncryptionTypes() );
222        }
223
224        KdcReqBody body = new KdcReqBody();
225
226        body.setFrom( new KerberosTime( clientTgtReq.getStartTime() ) );
227
228        PrincipalName cName = null;
229        try
230        {
231            cName = new PrincipalName( clientTgtReq.getCName(), PrincipalNameType.KRB_NT_PRINCIPAL );
232            body.setCName( cName );
233            body.setRealm( realm );
234            PrincipalName sName = new PrincipalName( clientTgtReq.getSName(), PrincipalNameType.KRB_NT_SRV_INST );
235            body.setSName( sName );
236        }
237        catch ( ParseException e )
238        {
239            throw new IllegalArgumentException( "Couldn't parse the given principals", e );
240        }
241
242        body.setTill( new KerberosTime( clientTgtReq.getExpiryTime() ) );
243        int currentNonce = nonceGenerator.nextInt();
244        body.setNonce( currentNonce );
245        body.setEType( clientTgtReq.getETypes() );
246        body.setKdcOptions( clientTgtReq.getOptions() );
247
248        List<HostAddress> lstAddresses = clientTgtReq.getHostAddresses();
249        if ( !lstAddresses.isEmpty() )
250        {
251            HostAddresses addresses = new HostAddresses();
252            for ( HostAddress h : lstAddresses )
253            {
254                addresses.addHostAddress( h );
255            }
256
257            body.setAddresses( addresses );
258        }
259
260        EncryptionType encryptionType = clientTgtReq.getETypes().iterator().next();
261        EncryptionKey clientKey = KerberosKeyFactory.string2Key( clientTgtReq.getClientPrincipal(),
262            clientTgtReq.getPassword(), encryptionType );
263
264        AsReq req = new AsReq();
265        req.setKdcReqBody( body );
266
267        if ( clientTgtReq.isPreAuthEnabled() )
268        {
269            PaEncTsEnc tmstmp = new PaEncTsEnc();
270            tmstmp.setPaTimestamp( new KerberosTime() );
271
272            EncryptedData paDataValue = cipherTextHandler.encrypt( clientKey, getEncoded( tmstmp ),
273                KeyUsage.AS_REQ_PA_ENC_TIMESTAMP_WITH_CKEY );
274
275            PaData paEncTstmp = new PaData();
276            paEncTstmp.setPaDataType( PaDataType.PA_ENC_TIMESTAMP );
277            paEncTstmp.setPaDataValue( getEncoded( paDataValue ) );
278
279            req.addPaData( paEncTstmp );
280        }
281
282        // Get the result from the future
283        try
284        {
285            connect();
286
287            // Read the response, waiting for it if not available immediately
288            // Get the response, blocking
289            KerberosMessage kdcRep = sendAndReceiveKrbMsg( req );
290
291            if ( kdcRep == null )
292            {
293                // We didn't received anything : this is an error
294                LOG.error( "Authentication failed : timeout occured" );
295                throw new KerberosException( ErrorType.KRB_ERR_GENERIC, TIME_OUT_ERROR );
296            }
297
298            if ( kdcRep instanceof KrbError )
299            {
300                // We have an error
301                LOG.debug( "Authentication failed : {}", kdcRep );
302                throw new KerberosException( ( KrbError ) kdcRep );
303            }
304
305            AsRep rep = ( AsRep ) kdcRep;
306
307            if ( !cName.getNameString().equals( rep.getCName().getNameString() ) )
308            {
309                throw new KerberosException( ErrorType.KDC_ERR_CLIENT_NAME_MISMATCH );
310            }
311
312            if ( !realm.equals( rep.getCRealm() ) )
313            {
314                throw new KerberosException( ErrorType.KRB_ERR_WRONG_REALM );
315            }
316
317            if ( encryptionType != rep.getEncPart().getEType() )
318            {
319                encryptionType = rep.getEncPart().getEType();
320                clientKey = KerberosKeyFactory.string2Key( clientTgtReq.getClientPrincipal(),
321                    clientTgtReq.getPassword(), encryptionType );
322            }
323
324            byte[] decryptedEncAsRepPart = cipherTextHandler.decrypt( clientKey, rep.getEncPart(),
325                KeyUsage.AS_REP_ENC_PART_WITH_CKEY );
326
327            EncKdcRepPart encKdcRepPart = null;
328            try
329            {
330                EncAsRepPart encAsRepPart = KerberosDecoder.decodeEncAsRepPart( decryptedEncAsRepPart );
331                encKdcRepPart = encAsRepPart.getEncKdcRepPart();
332            }
333            catch ( KerberosException e )
334            {
335                LOG.info( "Trying an encTgsRepPart instead" );
336                EncTgsRepPart encTgsRepPart = KerberosDecoder.decodeEncTgsRepPart( decryptedEncAsRepPart );
337                encKdcRepPart = encTgsRepPart.getEncKdcRepPart();
338            }
339
340            if ( currentNonce != encKdcRepPart.getNonce() )
341            {
342                throw new KerberosException( ErrorType.KRB_ERR_GENERIC,
343                    "received nonce didn't match with the nonce sent in the request" );
344            }
345
346            if ( !encKdcRepPart.getSName().getNameString().equals( clientTgtReq.getSName() ) )
347            {
348                throw new KerberosException( ErrorType.KDC_ERR_SERVER_NOMATCH );
349            }
350
351            if ( !encKdcRepPart.getSRealm().equals( clientTgtReq.getRealm() ) )
352            {
353                throw new KerberosException( ErrorType.KRB_ERR_GENERIC,
354                    "received server realm does not match with requested server realm" );
355            }
356
357            List<HostAddress> hosts = clientTgtReq.getHostAddresses();
358
359            if ( !hosts.isEmpty() )
360            {
361                HostAddresses addresses = encKdcRepPart.getClientAddresses();
362                for ( HostAddress h : hosts )
363                {
364                    if ( !addresses.contains( h ) )
365                    {
366                        throw new KerberosException( ErrorType.KRB_ERR_GENERIC,
367                            "requested client address" + h + " is not found in the ticket" );
368                    }
369                }
370            }
371
372            // Everything is fine, return the response
373            LOG.debug( "Authentication successful : {}", kdcRep );
374
375            return new TgTicket( rep.getTicket(), encKdcRepPart, rep.getCName().getNameString() );
376        }
377        catch ( KerberosException ke )
378        {
379            throw ke;
380        }
381        catch ( Exception e )
382        {
383            // We didn't received anything : this is an error
384            LOG.error( "Authentication failed" );
385            throw new KerberosException( ErrorType.KRB_ERR_GENERIC, TIME_OUT_ERROR );
386        }
387        finally
388        {
389            if ( channel != null )
390            {
391                try
392                {
393                    channel.close();
394                }
395                catch ( IOException e )
396                {
397                    LOG.warn( "Failed to close the channel", e );
398                }
399            }
400        }
401    }
402
403
404    private ServiceTicket getServiceTicket( ServiceTicketRequest srvTktReq ) throws KerberosException
405    {
406        String serverPrincipal = srvTktReq.getServerPrincipal();
407
408        // session key
409        EncryptionKey sessionKey = srvTktReq.getTgt().getSessionKey();
410
411        Authenticator authenticator = new Authenticator();
412
413        try
414        {
415            authenticator.setCName(
416                new PrincipalName( srvTktReq.getTgt().getClientName(), PrincipalNameType.KRB_NT_PRINCIPAL ) );
417        }
418        catch ( ParseException e )
419        {
420            throw new IllegalArgumentException( "Couldn't parse the given principal", e );
421        }
422
423        authenticator.setCRealm( srvTktReq.getTgt().getRealm() );
424        authenticator.setCTime( new KerberosTime() );
425        authenticator.setCusec( 0 );
426
427        if ( srvTktReq.getSubSessionKey() != null )
428        {
429            sessionKey = srvTktReq.getSubSessionKey();
430            authenticator.setSubKey( sessionKey );
431        }
432
433        EncryptedData authnData = cipherTextHandler.encrypt( sessionKey, getEncoded( authenticator ),
434            KeyUsage.TGS_REQ_PA_TGS_REQ_PADATA_AP_REQ_TGS_SESS_KEY );
435
436        ApReq apReq = new ApReq();
437
438        apReq.setAuthenticator( authnData );
439        apReq.setTicket( srvTktReq.getTgt().getTicket() );
440
441        apReq.setApOptions( srvTktReq.getApOptions() );
442
443        KdcReqBody tgsReqBody = new KdcReqBody();
444        tgsReqBody.setKdcOptions( srvTktReq.getKdcOptions() );
445        tgsReqBody.setRealm( KdcClientUtil.extractRealm( serverPrincipal ) );
446        tgsReqBody.setTill( getDefaultTill() );
447        int currentNonce = nonceGenerator.nextInt();
448        tgsReqBody.setNonce( currentNonce );
449        tgsReqBody.setEType( config.getEncryptionTypes() );
450
451        PrincipalName principalName = new PrincipalName( KdcClientUtil.extractName( serverPrincipal ),
452            KerberosPrincipal.KRB_NT_SRV_HST );
453        tgsReqBody.setSName( principalName );
454
455        TgsReq tgsReq = new TgsReq();
456        tgsReq.setKdcReqBody( tgsReqBody );
457
458        PaData authnHeader = new PaData();
459        authnHeader.setPaDataType( PaDataType.PA_TGS_REQ );
460        authnHeader.setPaDataValue( getEncoded( apReq ) );
461
462        tgsReq.addPaData( authnHeader );
463
464        // Get the result from the future
465        try
466        {
467            connect();
468
469            // Read the response, waiting for it if not available immediately
470            // Get the response, blocking
471            KerberosMessage kdcRep = sendAndReceiveKrbMsg( tgsReq );
472
473            if ( kdcRep == null )
474            {
475                // We didn't received anything : this is an error
476                LOG.error( "TGT request failed : timeout occured" );
477                throw new KerberosException( ErrorType.KRB_ERR_GENERIC, TIME_OUT_ERROR );
478            }
479
480            if ( kdcRep instanceof KrbError )
481            {
482                // We have an error
483                LOG.debug( "TGT request failed : {}", kdcRep );
484                throw new KerberosException( ( KrbError ) kdcRep );
485            }
486
487            TgsRep rep = ( TgsRep ) kdcRep;
488            byte[] decryptedData = cipherTextHandler.decrypt( sessionKey, rep.getEncPart(),
489                KeyUsage.TGS_REP_ENC_PART_TGS_SESS_KEY );
490            EncTgsRepPart encTgsRepPart = KerberosDecoder.decodeEncTgsRepPart( decryptedData );
491
492            if ( currentNonce != encTgsRepPart.getEncKdcRepPart().getNonce() )
493            {
494                throw new KerberosException( ErrorType.KRB_ERR_GENERIC,
495                    "received nonce didn't match with the nonce sent in the request" );
496            }
497
498            // Everything is fine, return the response
499            LOG.debug( "TGT request successful : {}", rep );
500
501            return new ServiceTicket( rep.getTicket(), encTgsRepPart.getEncKdcRepPart() );
502        }
503        catch ( KerberosException e )
504        {
505            throw e;
506        }
507        catch ( Exception te )
508        {
509            // We didn't receive anything : this is an error
510            LOG.error( "TGT request failed : timeout occured" );
511            throw new KerberosException( ErrorType.KRB_ERR_GENERIC, TIME_OUT_ERROR );
512        }
513        finally
514        {
515            if ( channel != null )
516            {
517                try
518                {
519                    channel.close();
520                }
521                catch ( IOException e )
522                {
523                    LOG.warn( "Failed to close the channel", e );
524                }
525            }
526        }
527    }
528
529
530    public ChangePasswordResult changePassword( String clientPrincipal, String oldPassword, String newPassword )
531        throws ChangePasswordException
532    {
533        KerberosChannel channel = null;
534
535        try
536        {
537            TgtRequest clientTgtReq = new TgtRequest();
538            clientTgtReq.setClientPrincipal( clientPrincipal );
539            clientTgtReq.setPassword( oldPassword );
540            clientTgtReq.setServerPrincipal( "kadmin/changepw@" + KdcClientUtil.extractRealm( clientPrincipal ) );
541
542            TgTicket tgt = getTgt( clientTgtReq );
543
544            ApReq apReq = new ApReq();
545            ApOptions options = new ApOptions();
546            apReq.setApOptions( options );
547            apReq.setTicket( tgt.getTicket() );
548
549            Authenticator authenticator = new Authenticator();
550            authenticator.setCName( new PrincipalName( tgt.getClientName(), PrincipalNameType.KRB_NT_PRINCIPAL ) );
551            authenticator.setCRealm( tgt.getRealm() );
552            KerberosTime ctime = new KerberosTime();
553            authenticator.setCTime( ctime );
554            authenticator.setCusec( 0 );
555            authenticator.setSeqNumber( nonceGenerator.nextInt() );
556
557            EncryptionKey subKey = RandomKeyFactory.getRandomKey( tgt.getEncKdcRepPart().getKey().getKeyType() );
558
559            authenticator.setSubKey( subKey );
560
561            EncryptedData authData = cipherTextHandler.encrypt( tgt.getSessionKey(), getEncoded( authenticator ),
562                KeyUsage.AP_REQ_AUTHNT_SESS_KEY );
563            apReq.setAuthenticator( authData );
564
565            KrbPriv privateMessage = new KrbPriv();
566
567            EncKrbPrivPart part = new EncKrbPrivPart();
568            part.setSenderAddress( new HostAddress( Network.LOOPBACK ) );
569            part.setSeqNumber( authenticator.getSeqNumber() );
570            part.setTimestamp( authenticator.getCtime() );
571
572            short changePwdPVNO = ChangePasswordRequest.PVNO;
573
574            if ( config.isUseLegacyChngPwdProtocol() )
575            {
576                part.setUserData( Strings.getBytesUtf8( newPassword ) );
577                changePwdPVNO = ChangePasswordRequest.OLD_PVNO;
578            }
579            else
580            {
581                ChangePasswdData chngPwdData = new ChangePasswdData();
582                chngPwdData.setNewPasswd( Strings.getBytesUtf8( newPassword ) );
583                byte[] data = getEncoded( chngPwdData );
584                part.setUserData( data );
585            }
586
587            EncryptedData encKrbPrivPartData = cipherTextHandler.encrypt( subKey, getEncoded( part ),
588                KeyUsage.KRB_PRIV_ENC_PART_CHOSEN_KEY );
589            privateMessage.setEncPart( encKrbPrivPartData );
590
591            ChangePasswordRequest req = new ChangePasswordRequest( changePwdPVNO, apReq, privateMessage );
592
593            channel = new KerberosChannel();
594            channel.openConnection( config.getHostName(), config.getPasswdPort(), config.getTimeout(),
595                config.isUseUdp() );
596
597            AbstractPasswordMessage reply = sendAndReceiveChngPwdMsg( req, channel );
598
599            if ( reply instanceof ChangePasswordError )
600            {
601                ChangePasswordError err = ( ChangePasswordError ) reply;
602
603                return new ChangePasswordResult( err.getKrbError().getEData() );
604            }
605
606            ChangePasswordReply chngPwdReply = ( ChangePasswordReply ) reply;
607
608            KrbPriv replyPriv = chngPwdReply.getPrivateMessage();
609            // the same subKey present in ApReq is used for encrypting the KrbPriv present in reply
610            byte[] data = cipherTextHandler.decrypt( subKey, replyPriv.getEncPart(),
611                KeyUsage.KRB_PRIV_ENC_PART_CHOSEN_KEY );
612            part = KerberosDecoder.decodeEncKrbPrivPart( data );
613
614            return new ChangePasswordResult( part.getUserData() );
615        }
616        catch ( ChangePasswordException e )
617        {
618            throw e;
619        }
620        catch ( Exception e )
621        {
622            LOG.warn( "failed to change the password", e );
623            throw new ChangePasswordException( ChangePasswdErrorType.KRB5_KPASSWD_HARDERROR, e );
624        }
625        finally
626        {
627            if ( channel != null )
628            {
629                try
630                {
631                    channel.close();
632                }
633                catch ( IOException e )
634                {
635                    LOG.warn( "Failed to close the channel", e );
636                }
637            }
638        }
639    }
640
641
642    private byte[] getEncoded( Asn1Object obj )
643    {
644        try
645        {
646            ByteBuffer buf = ByteBuffer.allocate( obj.computeLength() );
647            obj.encode( buf );
648
649            return buf.array();
650        }
651        catch ( Exception e )
652        {
653            // shouldn't happen, but if it does then log it and give  up
654            LOG.error( "Failed to encode the ASN.1 object {}", obj );
655            throw new RuntimeException( e );
656        }
657    }
658
659
660    private KerberosTime getDefaultTill()
661    {
662        return new KerberosTime( System.currentTimeMillis() + ( KerberosTime.MINUTE * 60 ) );
663    }
664
665
666    private KerberosMessage sendAndReceiveKrbMsg( KerberosMessage req ) throws Exception
667    {
668        ByteBuffer encodedBuf = KerberosEncoder.encode( req, channel.isUseTcp() );
669        encodedBuf.flip();
670
671        ByteBuffer repData = channel.sendAndReceive( encodedBuf );
672
673        KerberosMessageContainer kerberosMessageContainer = new KerberosMessageContainer();
674        kerberosMessageContainer.setStream( repData );
675        kerberosMessageContainer.setGathering( true );
676        kerberosMessageContainer.setTCP( channel.isUseTcp() );
677
678        return ( KerberosMessage ) KerberosDecoder.decode( kerberosMessageContainer );
679    }
680
681
682    private AbstractPasswordMessage sendAndReceiveChngPwdMsg( AbstractPasswordMessage req,
683        KerberosChannel chngPwdChannel ) throws Exception
684    {
685        ByteBuffer encodedBuf = ChangePasswordEncoder.encode( req, chngPwdChannel.isUseTcp() );
686        encodedBuf.flip();
687        ByteBuffer repData = chngPwdChannel.sendAndReceive( encodedBuf );
688
689        return ChangePasswordDecoder.decode( repData, chngPwdChannel.isUseTcp() );
690    }
691}