Frames | No Frames |
1: /* SaslInputStream.java -- 2: Copyright (C) 2003, 2006, 2010 Free Software Foundation, Inc. 3: 4: This file is a part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2 of the License, or (at 9: your option) any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; if not, write to the Free Software 18: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 19: USA 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package gnu.javax.crypto.sasl; 40: 41: import gnu.java.security.Configuration; 42: import gnu.java.security.util.Util; 43: 44: import java.io.IOException; 45: import java.io.InputStream; 46: import java.io.InterruptedIOException; 47: import java.util.logging.Logger; 48: 49: import javax.security.sasl.Sasl; 50: import javax.security.sasl.SaslClient; 51: import javax.security.sasl.SaslServer; 52: 53: /** 54: * An input stream that uses either a {@link SaslClient} or a {@link SaslServer} 55: * to process the data through these entities' security layer filter(s). 56: */ 57: public class SaslInputStream 58: extends InputStream 59: { 60: private static final Logger log = Configuration.DEBUG ? 61: Logger.getLogger(SaslInputStream.class.getName()) : null; 62: private SaslClient client; 63: private SaslServer server; 64: private int maxRawSendSize; 65: private InputStream source; 66: private byte[] internalBuf; 67: 68: public SaslInputStream(SaslClient client, InputStream source) 69: throws IOException 70: { 71: super(); 72: 73: this.client = client; 74: String size = (String) client.getNegotiatedProperty(Sasl.RAW_SEND_SIZE); 75: maxRawSendSize = Integer.parseInt(size); 76: server = null; 77: this.source = source; 78: } 79: 80: public SaslInputStream(SaslServer server, InputStream source) 81: throws IOException 82: { 83: super(); 84: 85: this.server = server; 86: String size = (String) server.getNegotiatedProperty(Sasl.RAW_SEND_SIZE); 87: maxRawSendSize = Integer.parseInt(size); 88: client = null; 89: this.source = source; 90: } 91: 92: public int available() throws IOException 93: { 94: return (internalBuf == null) ? 0 : internalBuf.length; 95: } 96: 97: public void close() throws IOException 98: { 99: source.close(); 100: } 101: 102: /** 103: * Reads the next byte of data from the input stream. The value byte is 104: * returned as an <code>int</code> in the range <code>0</code> to 105: * <code>255</code>. If no byte is available because the end of the stream 106: * has been reached, the value <code>-1</code> is returned. This method 107: * blocks until input data is available, the end of the stream is detected, or 108: * an exception is thrown. 109: * <p> 110: * From a SASL mechanism provider's perspective, if a security layer has been 111: * negotiated, the underlying <i>source</i> is expected to contain SASL 112: * buffers, as defined in RFC 2222. Four octets in network byte order in the 113: * front of each buffer identify the length of the buffer. The provider is 114: * responsible for performing any integrity checking or other processing on 115: * the buffer before returning the data as a stream of octets. For example, 116: * the protocol driver's request for a single octet from the stream might; 117: * i.e. an invocation of this method, may result in an entire SASL buffer 118: * being read and processed before that single octet can be returned. 119: * 120: * @return the next byte of data, or <code>-1</code> if the end of the 121: * stream is reached. 122: * @throws IOException if an I/O error occurs. 123: */ 124: public int read() throws IOException 125: { 126: int result = -1; 127: if (internalBuf != null && internalBuf.length > 0) 128: { 129: result = internalBuf[0] & 0xFF; 130: if (internalBuf.length == 1) 131: internalBuf = new byte[0]; 132: else 133: { 134: byte[] tmp = new byte[internalBuf.length - 1]; 135: System.arraycopy(internalBuf, 1, tmp, 0, tmp.length); 136: internalBuf = tmp; 137: } 138: } 139: else 140: { 141: byte[] buf = new byte[1]; 142: int check = read(buf); 143: result = (check > 0) ? (buf[0] & 0xFF) : -1; 144: } 145: return result; 146: } 147: 148: /** 149: * Reads up to <code>len</code> bytes of data from the underlying <i>source</i> 150: * input stream into an array of bytes. An attempt is made to read as many as 151: * <code>len</code> bytes, but a smaller number may be read, possibly zero. 152: * The number of bytes actually read is returned as an integer. 153: * <p> 154: * This method blocks until input data is available, end of file is detected, 155: * or an exception is thrown. 156: * <p> 157: * If <code>b</code> is <code>null</code>, a {@link NullPointerException} 158: * is thrown. 159: * <p> 160: * If <code>off</code> is negative, or <code>len</code> is negative, or 161: * <code>off+len</code> is greater than the length of the array 162: * <code>b</code>, then an {@link IndexOutOfBoundsException} is thrown. 163: * <p> 164: * If <code>len</code> is zero, then no bytes are read and <code>0</code> 165: * is returned; otherwise, there is an attempt to read at least one byte. If 166: * no byte is available because the stream is at end of file, the value 167: * <code>-1</code> is returned; otherwise, at least one byte is read and 168: * stored into <code>b</code>. 169: * <p> 170: * The first byte read is stored into element <code>b[off]</code>, the next 171: * one into <code>b[off+1]</code>, and so on. The number of bytes read is, 172: * at most, equal to <code>len</code>. Let <code>k</code> be the number 173: * of bytes actually read; these bytes will be stored in elements 174: * <code>b[off]</code> through <code>b[off+k-1]</code>, leaving elements 175: * <code>b[off+k]</code> through <code>b[off+len-1]</code> unaffected. 176: * <p> 177: * In every case, elements <code>b[0]</code> through <code>b[off]</code> 178: * and elements <code>b[off+len]</code> through <code>b[b.length-1]</code> 179: * are unaffected. 180: * <p> 181: * If the first byte cannot be read for any reason other than end of file, 182: * then an {@link IOException} is thrown. In particular, an 183: * {@link IOException} is thrown if the input stream has been closed. 184: * <p> 185: * From the SASL mechanism provider's perspective, if a security layer has 186: * been negotiated, the underlying <i>source</i> is expected to contain SASL 187: * buffers, as defined in RFC 2222. Four octets in network byte order in the 188: * front of each buffer identify the length of the buffer. The provider is 189: * responsible for performing any integrity checking or other processing on 190: * the buffer before returning the data as a stream of octets. The protocol 191: * driver's request for a single octet from the stream might result in an 192: * entire SASL buffer being read and processed before that single octet can be 193: * returned. 194: * 195: * @param b the buffer into which the data is read. 196: * @param off the start offset in array <code>b</code> at which the data is 197: * wricodeen. 198: * @param len the maximum number of bytes to read. 199: * @return the total number of bytes read into the buffer, or <code>-1</code> 200: * if there is no more data because the end of the stream has been 201: * reached. 202: * @throws IOException if an I/O error occurs. 203: */ 204: public int read(byte[] b, int off, int len) throws IOException 205: { 206: if (Configuration.DEBUG) 207: log.entering(this.getClass().getName(), "read", new Object[] { 208: b, Integer.valueOf(off), Integer.valueOf(len) 209: }); 210: if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) 211: || ((off + len) < 0)) 212: throw new IndexOutOfBoundsException("off=" + off + ", len=" + len 213: + ", b.length=" + b.length); 214: if (len == 0) 215: { 216: if (Configuration.DEBUG) 217: log.exiting(this.getClass().getName(), "read", Integer.valueOf(0)); 218: return 0; 219: } 220: if (Configuration.DEBUG) 221: log.finer("Available: " + available()); 222: int result = 0; 223: if (internalBuf == null || internalBuf.length < 1) 224: try 225: { 226: internalBuf = readSaslBuffer(); 227: if (internalBuf == null) 228: { 229: if (Configuration.DEBUG) 230: { 231: log.finer("Underlying stream empty. Returning -1"); 232: log.exiting(this.getClass().getName(), "read", 233: Integer.valueOf(-1)); 234: } 235: return -1; 236: } 237: } 238: catch (InterruptedIOException x) 239: { 240: if (Configuration.DEBUG) 241: { 242: log.finer("Reading thread was interrupted. Returning -1"); 243: log.throwing(this.getClass().getName(), "read", x); 244: log.exiting(this.getClass().getName(), "read", 245: Integer.valueOf(-1)); 246: } 247: return -1; 248: } 249: if (len <= internalBuf.length) 250: { 251: result = len; 252: System.arraycopy(internalBuf, 0, b, off, len); 253: if (len == internalBuf.length) 254: internalBuf = null; 255: else 256: { 257: byte[] tmp = new byte[internalBuf.length - len]; 258: System.arraycopy(internalBuf, len, tmp, 0, tmp.length); 259: internalBuf = tmp; 260: } 261: } 262: else 263: { 264: // first copy the available bytes to b 265: result = internalBuf.length; 266: System.arraycopy(internalBuf, 0, b, off, result); 267: internalBuf = null; 268: off += result; 269: len -= result; 270: int remaining; // count of bytes remaining in buffer after an iteration 271: int delta; // count of bytes moved to b after an iteration 272: int datalen; 273: byte[] data; 274: while (len > 0) 275: // we need to read SASL buffers, as long as there are at least 276: // 4 bytes available at the source 277: if (source.available() > 3) 278: { 279: // process a buffer 280: data = readSaslBuffer(); 281: if (data == null) 282: { 283: if (Configuration.DEBUG) 284: log.finer("Underlying stream exhausted. Breaking..."); 285: break; 286: } 287: datalen = data.length; 288: // copy [part of] the result to b 289: remaining = (datalen <= len) ? 0 : datalen - len; 290: delta = datalen - remaining; 291: System.arraycopy(data, 0, b, off, delta); 292: if (remaining > 0) 293: { 294: internalBuf = new byte[remaining]; 295: System.arraycopy(data, delta, internalBuf, 0, remaining); 296: } 297: // update off, result and len 298: off += delta; 299: result += delta; 300: len -= delta; 301: } 302: else 303: { // nothing much we can do except return what we have 304: if (Configuration.DEBUG) 305: log.finer("Not enough bytes in source to read a buffer. Breaking..."); 306: break; 307: } 308: } 309: if (Configuration.DEBUG) 310: { 311: log.finer("Remaining: " 312: + (internalBuf == null ? 0 : internalBuf.length)); 313: log.exiting(this.getClass().getName(), "read()", String.valueOf(result)); 314: } 315: return result; 316: } 317: 318: /** 319: * Reads a SASL buffer from the underlying source if at least 4 bytes are 320: * available. 321: * 322: * @return the byte[] of decoded buffer contents, or null if the underlying 323: * source was exhausted. 324: * @throws IOException if an I/O exception occurs during the operation. 325: */ 326: private byte[] readSaslBuffer() throws IOException 327: { 328: if (Configuration.DEBUG) 329: log.entering(this.getClass().getName(), "readSaslBuffer()"); 330: int realLength; // check if we read as many bytes as we're supposed to 331: byte[] result = new byte[4]; 332: try 333: { 334: realLength = source.read(result); 335: if (realLength == -1) 336: { 337: if (Configuration.DEBUG) 338: log.exiting(this.getClass().getName(), "readSaslBuffer"); 339: return null; 340: } 341: } 342: catch (IOException x) 343: { 344: if (Configuration.DEBUG) 345: log.throwing(this.getClass().getName(), "readSaslBuffer", x); 346: throw x; 347: } 348: if (realLength != 4) 349: throw new IOException("Was expecting 4 but found " + realLength); 350: int bufferLength = result[0] << 24 351: | (result[1] & 0xFF) << 16 352: | (result[2] & 0xFF) << 8 353: | (result[3] & 0xFF); 354: if (Configuration.DEBUG) 355: log.finer("SASL buffer size: " + bufferLength); 356: if (bufferLength > maxRawSendSize || bufferLength < 0) 357: throw new SaslEncodingException("SASL buffer (security layer) too long"); 358: 359: result = new byte[bufferLength]; 360: try 361: { 362: realLength = source.read(result); 363: } 364: catch (IOException x) 365: { 366: if (Configuration.DEBUG) 367: log.throwing(this.getClass().getName(), "readSaslBuffer", x); 368: throw x; 369: } 370: if (realLength != bufferLength) 371: throw new IOException("Was expecting " + bufferLength + " but found " 372: + realLength); 373: if (Configuration.DEBUG) 374: { 375: log.finer("Incoming buffer (before security) (hex): " 376: + Util.dumpString(result)); 377: log.finer("Incoming buffer (before security) (str): \"" 378: + new String(result) + "\""); 379: } 380: if (client != null) 381: result = client.unwrap(result, 0, realLength); 382: else 383: result = server.unwrap(result, 0, realLength); 384: if (Configuration.DEBUG) 385: { 386: log.finer("Incoming buffer (after security) (hex): " 387: + Util.dumpString(result)); 388: log.finer("Incoming buffer (after security) (str): \"" 389: + new String(result) + "\""); 390: log.exiting(this.getClass().getName(), "readSaslBuffer"); 391: } 392: return result; 393: } 394: }