1/*
2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 *
28 *  (C) Copyright IBM Corp. 1999 All Rights Reserved.
29 *  Copyright 1997 The Open Group Research Institute.  All rights reserved.
30 */
31
32package sun.security.krb5.internal.rcache;
33
34import java.io.IOException;
35import java.nio.BufferUnderflowException;
36import java.nio.ByteBuffer;
37import java.nio.ByteOrder;
38import java.nio.channels.SeekableByteChannel;
39import java.nio.charset.StandardCharsets;
40import java.util.StringTokenizer;
41
42/**
43 * The class represents an old style replay cache entry. It is only used in
44 * a dfl file.
45 *
46 * @author Sun/Oracle
47 * @author Yanni Zhang
48 */
49public class AuthTime {
50    final int ctime;
51    final int cusec;
52    final String client;
53    final String server;
54
55    /**
56     * Constructs an <code>AuthTime</code>.
57     */
58    public AuthTime(String client, String server,
59            int ctime, int cusec) {
60        this.ctime = ctime;
61        this.cusec = cusec;
62        this.client = client;
63        this.server = server;
64    }
65
66    @Override
67    public String toString() {
68        return String.format("%d/%06d/----/%s", ctime, cusec, client);
69    }
70
71    // Methods used when saved in a dfl file. See DflCache.java
72
73    /**
74     * Reads an LC style string from a channel, which is a int32 length
75     * plus a UTF-8 encoded string possibly ends with \0.
76     * @throws IOException if there is a format error
77     * @throws BufferUnderflowException if goes beyond the end
78     */
79    private static String readStringWithLength(SeekableByteChannel chan)
80            throws IOException {
81        ByteBuffer bb = ByteBuffer.allocate(4);
82        bb.order(ByteOrder.nativeOrder());
83        chan.read(bb);
84        bb.flip();
85        int len = bb.getInt();
86        if (len > 1024) {
87            // Memory attack? The string should be fairly short.
88            throw new IOException("Invalid string length");
89        }
90        bb = ByteBuffer.allocate(len);
91        if (chan.read(bb) != len) {
92            throw new IOException("Not enough string");
93        }
94        byte[] data = bb.array();
95        return (data[len-1] == 0)?
96                new String(data, 0, len-1, StandardCharsets.UTF_8):
97                new String(data, StandardCharsets.UTF_8);
98    }
99
100    /**
101     * Reads an AuthTime or AuthTimeWithHash object from a channel.
102     * @throws IOException if there is a format error
103     * @throws BufferUnderflowException if goes beyond the end
104     */
105    public static AuthTime readFrom(SeekableByteChannel chan)
106            throws IOException {
107        String client = readStringWithLength(chan);
108        String server = readStringWithLength(chan);
109        ByteBuffer bb = ByteBuffer.allocate(8);
110        chan.read(bb);
111        bb.order(ByteOrder.nativeOrder());
112        int cusec = bb.getInt(0);
113        int ctime = bb.getInt(4);
114        if (client.isEmpty()) {
115            StringTokenizer st = new StringTokenizer(server, " :");
116            if (st.countTokens() != 6) {
117                throw new IOException("Incorrect rcache style");
118            }
119            String hashAlg = st.nextToken();
120            String hash = st.nextToken();
121            st.nextToken();
122            client = st.nextToken();
123            st.nextToken();
124            server = st.nextToken();
125            return new AuthTimeWithHash(
126                    client, server, ctime, cusec, hashAlg, hash);
127        } else {
128            return new AuthTime(
129                    client, server, ctime, cusec);
130        }
131    }
132
133    /**
134     * Encodes to be used in a dfl file
135     */
136    protected byte[] encode0(String cstring, String sstring) {
137        byte[] c = cstring.getBytes(StandardCharsets.UTF_8);;
138        byte[] s = sstring.getBytes(StandardCharsets.UTF_8);;
139        byte[] zero = new byte[1];
140        int len = 4 + c.length + 1 + 4 + s.length + 1 + 4 + 4;
141        ByteBuffer bb = ByteBuffer.allocate(len)
142                .order(ByteOrder.nativeOrder());
143        bb.putInt(c.length+1).put(c).put(zero)
144                .putInt(s.length+1).put(s).put(zero)
145                .putInt(cusec).putInt(ctime);
146        return bb.array();
147    }
148
149    /**
150     * Encodes to be used in a dfl file
151     * @param withHash useless here
152     */
153    public byte[] encode(boolean withHash) {
154        return encode0(client, server);
155    }
156}
157