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.credentials.cache;
021
022
023import java.io.DataOutputStream;
024import java.io.IOException;
025import java.io.OutputStream;
026import java.util.List;
027
028import org.apache.directory.api.util.Strings;
029import org.apache.directory.shared.kerberos.KerberosTime;
030import org.apache.directory.shared.kerberos.codec.KerberosEncoder;
031import org.apache.directory.shared.kerberos.components.AuthorizationData;
032import org.apache.directory.shared.kerberos.components.AuthorizationDataEntry;
033import org.apache.directory.shared.kerberos.components.EncryptionKey;
034import org.apache.directory.shared.kerberos.components.HostAddress;
035import org.apache.directory.shared.kerberos.components.HostAddresses;
036import org.apache.directory.shared.kerberos.components.PrincipalName;
037import org.apache.directory.shared.kerberos.messages.Ticket;
038
039
040/**
041 * Writing credentials cache according to FCC format by reference the following
042 * https://www.gnu.org/software/shishi/manual/html_node/The-Credential-Cache-Binary-File-Format.html
043 * 
044 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
045 */
046public class CacheOutputStream extends DataOutputStream
047{
048
049    public CacheOutputStream( OutputStream out )
050    {
051        super( out );
052    }
053
054
055    public void write( CredentialsCache credCache ) throws IOException
056    {
057        /**
058         * Currently we always write using this version to limit the test effort.
059         * This version seems to be the easiest to be compatible with MIT tools.
060         * In future we might allow to specify the format version to write if necessary. 
061         */
062        int writeVersion = CredentialsCacheConstants.FCC_FVNO_3;
063
064        writeVersion( writeVersion );
065
066        if ( writeVersion == CredentialsCacheConstants.FCC_FVNO_4 )
067        {
068            writeTags( credCache.getTags() );
069        }
070
071        writePrincipal( credCache.getPrimaryPrincipalName(), writeVersion );
072
073        List<Credentials> credentialsList = credCache.getCredsList();
074        if ( credentialsList != null )
075        {
076            for ( Credentials cred : credentialsList )
077            {
078                writeCredentials( cred, writeVersion );
079            }
080        }
081    }
082
083
084    private void writeVersion( int version ) throws IOException
085    {
086        writeShort( version );
087    }
088
089
090    private void writeTags( List<Tag> tags ) throws IOException
091    {
092        int length = 0;
093        if ( tags != null )
094        {
095            for ( Tag tag : tags )
096            {
097                if ( tag.tag != CredentialsCacheConstants.FCC_TAG_DELTATIME )
098                {
099                    continue;
100                }
101                length += tag.length;
102            }
103        }
104
105        writeShort( length );
106
107        if ( tags != null )
108        {
109            for ( Tag tag : tags )
110            {
111                if ( tag.tag != CredentialsCacheConstants.FCC_TAG_DELTATIME )
112                {
113                    continue;
114                }
115                writeTag( tag );
116            }
117        }
118    }
119
120
121    private void writeTag( Tag tag ) throws IOException
122    {
123        writeShort( tag.tag );
124        writeShort( tag.length );
125        writeInt( tag.time );
126        writeInt( tag.usec );
127    }
128
129
130    private void writePrincipal( PrincipalName pname, int version ) throws IOException
131    {
132        int num = pname.getNames().size();
133
134        if ( version != CredentialsCacheConstants.FCC_FVNO_1 )
135        {
136            writeInt( pname.getNameType().getValue() );
137        }
138        else
139        {
140            num++;
141        }
142
143        writeInt( num );
144
145        if ( pname.getRealm() != null )
146        {
147            byte[] realmBytes = null;
148            realmBytes = Strings.getBytesUtf8( pname.getRealm() );
149            writeInt( realmBytes.length );
150            write( realmBytes );
151        }
152        else
153        {
154            writeInt( 0 );
155        }
156
157        byte[] bytes = null;
158        for ( int i = 0; i < pname.getNames().size(); i++ )
159        {
160            bytes = Strings.getBytesUtf8( pname.getNames().get( i ) );
161            writeInt( bytes.length );
162            write( bytes );
163        }
164    }
165
166
167    private void writeCredentials( Credentials creds, int version ) throws IOException
168    {
169        writePrincipal( creds.getClientName(), version );
170        writePrincipal( creds.getServerName(), version );
171        writeKey( creds.getKey(), version );
172
173        writeKerberosTime( creds.getAuthTime() );
174        writeKerberosTime( creds.getStartTime() );
175        writeKerberosTime( creds.getEndTime() );
176        writeKerberosTime( creds.getRenewTill() );
177
178        writeByte( creds.isEncInSKey() ? 1 : 0 );
179
180        writeInt( creds.getFlags().getIntValue() );
181
182        writeAddrs( creds.getClientAddresses() );
183        writeAuth( creds.getAuthzData() );
184
185        writeTicket( creds.getTicket() );
186        writeTicket( creds.getSecondTicket() );
187    }
188
189
190    private void writeKerberosTime( KerberosTime ktime ) throws IOException
191    {
192        int time = 0;
193        if ( ktime != null )
194        {
195            time = ( int ) ( ktime.getTime() / 1000 );
196        }
197        writeInt( time );
198    }
199
200
201    private void writeKey( EncryptionKey key, int version ) throws IOException
202    {
203        writeShort( key.getKeyType().getValue() );
204        if ( version == CredentialsCacheConstants.FCC_FVNO_3 )
205        {
206            writeShort( key.getKeyType().getValue() );
207        }
208        // It's not correct with "uint16_t keylen", instead "uint32_t keylen" in keyblock       
209        writeInt( key.getKeyValue().length );
210        write( key.getKeyValue() );
211    }
212
213
214    private void writeAddrs( HostAddresses addresses ) throws IOException
215    {
216        if ( addresses == null )
217        {
218            writeInt( 0 );
219        }
220        else
221        {
222            HostAddress[] addrs = addresses.getAddresses();
223            write( addrs.length );
224            for ( int i = 0; i < addrs.length; i++ )
225            {
226                write( addrs[i].getAddrType().getValue() );
227                write( addrs[i].getAddress().length );
228                write( addrs[i].getAddress(), 0,
229                    addrs[i].getAddress().length );
230            }
231        }
232    }
233
234
235    private void writeAuth( AuthorizationData authData ) throws IOException
236    {
237        if ( authData == null )
238        {
239            writeInt( 0 );
240        }
241        else
242        {
243            for ( AuthorizationDataEntry ade : authData.getAuthorizationData() )
244            {
245                write( ade.getAdType().getValue() );
246                write( ade.getAdData().length );
247                write( ade.getAdData() );
248            }
249        }
250    }
251
252
253    private void writeTicket( Ticket t ) throws IOException
254    {
255        if ( t == null )
256        {
257            writeInt( 0 );
258        }
259        else
260        {
261            byte[] bytes = KerberosEncoder.encode( t, false ).array();
262            writeInt( bytes.length );
263            write( bytes );
264        }
265    }
266}