1 /* 2 * Copyright (c) 2001, 2003, 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 package com.sun.jndi.ldap.sasl; 27 28 import javax.security.sasl.Sasl; 29 import javax.security.sasl.SaslClient; 30 import javax.security.sasl.SaslException; 31 import java.io.IOException; 32 import java.io.EOFException; 33 import java.io.InputStream; 34 35 /** 36 * This class is used by clients of Java SASL that need to create an input stream 37 * that uses SaslClient's unwrap() method to decode the SASL buffers 38 * sent by the SASL server. 39 * 40 * Extend from InputStream instead of FilterInputStream because 41 * we need to override less methods in InputStream. That is, the 42 * behavior of the default implementations in InputStream matches 43 * more closely with the behavior we want in SaslInputStream. 44 * 45 * @author Rosanna Lee 46 */ 47 public class SaslInputStream extends InputStream { 48 private static final boolean debug = false; 49 50 private byte[] saslBuffer; // buffer for storing raw bytes 51 private byte[] lenBuf = new byte[4]; // buffer for storing length 52 53 private byte[] buf = new byte[0]; // buffer for storing processed bytes 54 // Initialized to empty buffer 55 private int bufPos = 0; // read position in buf 56 private InputStream in; // underlying input stream 57 private SaslClient sc; 58 private int recvMaxBufSize = 65536; 59 SaslInputStream(SaslClient sc, InputStream in)60 SaslInputStream(SaslClient sc, InputStream in) throws SaslException { 61 super(); 62 this.in = in; 63 this.sc = sc; 64 65 String str = (String) sc.getNegotiatedProperty(Sasl.MAX_BUFFER); 66 if (str != null) { 67 try { 68 recvMaxBufSize = Integer.parseInt(str); 69 } catch (NumberFormatException e) { 70 throw new SaslException(Sasl.MAX_BUFFER + 71 " property must be numeric string: " + str); 72 } 73 } 74 saslBuffer = new byte[recvMaxBufSize]; 75 } 76 read()77 public int read() throws IOException { 78 byte[] inBuf = new byte[1]; 79 int count = read(inBuf, 0, 1); 80 if (count > 0) { 81 return inBuf[0]; 82 } else { 83 return -1; 84 } 85 } 86 read(byte[] inBuf, int start, int count)87 public int read(byte[] inBuf, int start, int count) throws IOException { 88 89 if (bufPos >= buf.length) { 90 int actual = fill(); // read and unwrap next SASL buffer 91 while (actual == 0) { // ignore zero length content 92 actual = fill(); 93 } 94 if (actual == -1) { 95 return -1; // EOF 96 } 97 } 98 99 int avail = buf.length - bufPos; 100 if (count > avail) { 101 // Requesting more that we have stored 102 // Return all that we have; next invocation of read() will 103 // trigger fill() 104 System.arraycopy(buf, bufPos, inBuf, start, avail); 105 bufPos = buf.length; 106 return avail; 107 } else { 108 // Requesting less than we have stored 109 // Return all that was requested 110 System.arraycopy(buf, bufPos, inBuf, start, count); 111 bufPos += count; 112 return count; 113 } 114 } 115 116 /** 117 * Fills the buf with more data by reading a SASL buffer, unwrapping it, 118 * and leaving the bytes in buf for read() to return. 119 * @return The number of unwrapped bytes available 120 */ fill()121 private int fill() throws IOException { 122 // Read in length of buffer 123 int actual = readFully(lenBuf, 4); 124 if (actual != 4) { 125 return -1; 126 } 127 int len = networkByteOrderToInt(lenBuf, 0, 4); 128 129 if (len > recvMaxBufSize) { 130 throw new IOException( 131 len + "exceeds the negotiated receive buffer size limit:" + 132 recvMaxBufSize); 133 } 134 135 if (debug) { 136 System.err.println("reading " + len + " bytes from network"); 137 } 138 139 // Read SASL buffer 140 actual = readFully(saslBuffer, len); 141 if (actual != len) { 142 throw new EOFException("Expecting to read " + len + 143 " bytes but got " + actual + " bytes before EOF"); 144 } 145 146 // Unwrap 147 buf = sc.unwrap(saslBuffer, 0, len); 148 149 bufPos = 0; 150 151 return buf.length; 152 } 153 154 /** 155 * Read requested number of bytes before returning. 156 * @return The number of bytes actually read; -1 if none read 157 */ readFully(byte[] inBuf, int total)158 private int readFully(byte[] inBuf, int total) throws IOException { 159 int count, pos = 0; 160 161 if (debug) { 162 System.err.println("readFully " + total + " from " + in); 163 } 164 165 while (total > 0) { 166 count = in.read(inBuf, pos, total); 167 168 if (debug) { 169 System.err.println("readFully read " + count); 170 } 171 172 if (count == -1 ) { 173 return (pos == 0? -1 : pos); 174 } 175 pos += count; 176 total -= count; 177 } 178 return pos; 179 } 180 available()181 public int available() throws IOException { 182 return buf.length - bufPos; 183 } 184 close()185 public void close() throws IOException { 186 SaslException save = null; 187 try { 188 sc.dispose(); // Dispose of SaslClient's state 189 } catch (SaslException e) { 190 // Save exception for throwing after closing 'in' 191 save = e; 192 } 193 194 in.close(); // Close underlying input stream 195 196 if (save != null) { 197 throw save; 198 } 199 } 200 201 /** 202 * Returns the integer represented by 4 bytes in network byte order. 203 */ 204 // Copied from com.sun.security.sasl.util.SaslImpl. networkByteOrderToInt(byte[] buf, int start, int count)205 private static int networkByteOrderToInt(byte[] buf, int start, int count) { 206 if (count > 4) { 207 throw new IllegalArgumentException("Cannot handle more than 4 bytes"); 208 } 209 210 int answer = 0; 211 212 for (int i = 0; i < count; i++) { 213 answer <<= 8; 214 answer |= ((int)buf[start+i] & 0xff); 215 } 216 return answer; 217 } 218 } 219