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 
  32 package sun.security.krb5.internal.rcache;
  33 
  34 import java.io.IOException;
  35 import java.nio.BufferUnderflowException;
  36 import java.nio.ByteBuffer;
  37 import java.nio.ByteOrder;
  38 import java.nio.channels.SeekableByteChannel;
  39 import java.nio.charset.StandardCharsets;
  40 import 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  */
  49 public 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             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, 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 }