Frames | No Frames |
1: /* SaslInputStream.java -- 2: Copyright (C) 2003, 2006 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 = Logger.getLogger(SaslInputStream.class.getName()); 61: private SaslClient client; 62: private SaslServer server; 63: private int maxRawSendSize; 64: private InputStream source; 65: private byte[] internalBuf; 66: 67: public SaslInputStream(SaslClient client, InputStream source) 68: throws IOException 69: { 70: super(); 71: 72: this.client = client; 73: String size = (String) client.getNegotiatedProperty(Sasl.RAW_SEND_SIZE); 74: maxRawSendSize = Integer.parseInt(size); 75: server = null; 76: this.source = source; 77: } 78: 79: public SaslInputStream(SaslServer server, InputStream source) 80: throws IOException 81: { 82: super(); 83: 84: this.server = server; 85: String size = (String) server.getNegotiatedProperty(Sasl.RAW_SEND_SIZE); 86: maxRawSendSize = Integer.parseInt(size); 87: client = null; 88: this.source = source; 89: } 90: 91: public int available() throws IOException 92: { 93: return (internalBuf == null) ? 0 : internalBuf.length; 94: } 95: 96: public void close() throws IOException 97: { 98: source.close(); 99: } 100: 101: /** 102: * Reads the next byte of data from the input stream. The value byte is 103: * returned as an <code>int</code> in the range <code>0</code> to 104: * <code>255</code>. If no byte is available because the end of the stream 105: * has been reached, the value <code>-1</code> is returned. This method 106: * blocks until input data is available, the end of the stream is detected, or 107: * an exception is thrown. 108: * <p> 109: * From a SASL mechanism provider's perspective, if a security layer has been 110: * negotiated, the underlying <i>source</i> is expected to contain SASL 111: * buffers, as defined in RFC 2222. Four octets in network byte order in the 112: * front of each buffer identify the length of the buffer. The provider is 113: * responsible for performing any integrity checking or other processing on 114: * the buffer before returning the data as a stream of octets. For example, 115: * the protocol driver's request for a single octet from the stream might; 116: * i.e. an invocation of this method, may result in an entire SASL buffer 117: * being read and processed before that single octet can be returned. 118: * 119: * @return the next byte of data, or <code>-1</code> if the end of the 120: * stream is reached. 121: * @throws IOException if an I/O error occurs. 122: */ 123: public int read() throws IOException 124: { 125: int result = -1; 126: if (internalBuf != null && internalBuf.length > 0) 127: { 128: result = internalBuf[0] & 0xFF; 129: if (internalBuf.length == 1) 130: internalBuf = new byte[0]; 131: else 132: { 133: byte[] tmp = new byte[internalBuf.length - 1]; 134: System.arraycopy(internalBuf, 1, tmp, 0, tmp.length); 135: internalBuf = tmp; 136: } 137: } 138: else 139: { 140: byte[] buf = new byte[1]; 141: int check = read(buf); 142: result = (check > 0) ? (buf[0] & 0xFF) : -1; 143: } 144: return result; 145: } 146: 147: /** 148: * Reads up to <code>len</code> bytes of data from the underlying <i>source</i> 149: * input stream into an array of bytes. An attempt is made to read as many as 150: * <code>len</code> bytes, but a smaller number may be read, possibly zero. 151: * The number of bytes actually read is returned as an integer. 152: * <p> 153: * This method blocks until input data is available, end of file is detected, 154: * or an exception is thrown. 155: * <p> 156: * If <code>b</code> is <code>null</code>, a {@link NullPointerException} 157: * is thrown. 158: * <p> 159: * If <code>off</code> is negative, or <code>len</code> is negative, or 160: * <code>off+len</code> is greater than the length of the array 161: * <code>b</code>, then an {@link IndexOutOfBoundsException} is thrown. 162: * <p> 163: * If <code>len</code> is zero, then no bytes are read and <code>0</code> 164: * is returned; otherwise, there is an attempt to read at least one byte. If 165: * no byte is available because the stream is at end of file, the value 166: * <code>-1</code> is returned; otherwise, at least one byte is read and 167: * stored into <code>b</code>. 168: * <p> 169: * The first byte read is stored into element <code>b[off]</code>, the next 170: * one into <code>b[off+1]</code>, and so on. The number of bytes read is, 171: * at most, equal to <code>len</code>. Let <code>k</code> be the number 172: * of bytes actually read; these bytes will be stored in elements 173: * <code>b[off]</code> through <code>b[off+k-1]</code>, leaving elements 174: * <code>b[off+k]</code> through <code>b[off+len-1]</code> unaffected. 175: * <p> 176: * In every case, elements <code>b[0]</code> through <code>b[off]</code> 177: * and elements <code>b[off+len]</code> through <code>b[b.length-1]</code> 178: * are unaffected. 179: * <p> 180: * If the first byte cannot be read for any reason other than end of file, 181: * then an {@link IOException} is thrown. In particular, an 182: * {@link IOException} is thrown if the input stream has been closed. 183: * <p> 184: * From the SASL mechanism provider's perspective, if a security layer has 185: * been negotiated, the underlying <i>source</i> is expected to contain SASL 186: * buffers, as defined in RFC 2222. Four octets in network byte order in the 187: * front of each buffer identify the length of the buffer. The provider is 188: * responsible for performing any integrity checking or other processing on 189: * the buffer before returning the data as a stream of octets. The protocol 190: * driver's request for a single octet from the stream might result in an 191: * entire SASL buffer being read and processed before that single octet can be 192: * returned. 193: * 194: * @param b the buffer into which the data is read. 195: * @param off the start offset in array <code>b</code> at which the data is 196: * wricodeen. 197: * @param len the maximum number of bytes to read. 198: * @return the total number of bytes read into the buffer, or <code>-1</code> 199: * if there is no more data because the end of the stream has been 200: * reached. 201: * @throws IOException if an I/O error occurs. 202: */ 203: public int read(byte[] b, int off, int len) throws IOException 204: { 205: if (Configuration.DEBUG) 206: log.entering(this.getClass().getName(), "read", new Object[] { 207: b, Integer.valueOf(off), Integer.valueOf(len) 208: }); 209: if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) 210: || ((off + len) < 0)) 211: throw new IndexOutOfBoundsException("off=" + off + ", len=" + len 212: + ", b.length=" + b.length); 213: if (len == 0) 214: { 215: if (Configuration.DEBUG) 216: log.exiting(this.getClass().getName(), "read", Integer.valueOf(0)); 217: return 0; 218: } 219: if (Configuration.DEBUG) 220: log.finer("Available: " + available()); 221: int result = 0; 222: if (internalBuf == null || internalBuf.length < 1) 223: try 224: { 225: internalBuf = readSaslBuffer(); 226: if (internalBuf == null) 227: { 228: if (Configuration.DEBUG) 229: { 230: log.finer("Underlying stream empty. Returning -1"); 231: log.exiting(this.getClass().getName(), "read", 232: Integer.valueOf(-1)); 233: } 234: return -1; 235: } 236: } 237: catch (InterruptedIOException x) 238: { 239: if (Configuration.DEBUG) 240: { 241: log.finer("Reading thread was interrupted. Returning -1"); 242: log.throwing(this.getClass().getName(), "read", x); 243: log.exiting(this.getClass().getName(), "read", 244: Integer.valueOf(-1)); 245: } 246: return -1; 247: } 248: if (len <= internalBuf.length) 249: { 250: result = len; 251: System.arraycopy(internalBuf, 0, b, off, len); 252: if (len == internalBuf.length) 253: internalBuf = null; 254: else 255: { 256: byte[] tmp = new byte[internalBuf.length - len]; 257: System.arraycopy(internalBuf, len, tmp, 0, tmp.length); 258: internalBuf = tmp; 259: } 260: } 261: else 262: { 263: // first copy the available bytes to b 264: result = internalBuf.length; 265: System.arraycopy(internalBuf, 0, b, off, result); 266: internalBuf = null; 267: off += result; 268: len -= result; 269: int remaining; // count of bytes remaining in buffer after an iteration 270: int delta; // count of bytes moved to b after an iteration 271: int datalen; 272: byte[] data; 273: while (len > 0) 274: // we need to read SASL buffers, as long as there are at least 275: // 4 bytes available at the source 276: if (source.available() > 3) 277: { 278: // process a buffer 279: data = readSaslBuffer(); 280: if (data == null) 281: { 282: if (Configuration.DEBUG) 283: log.finer("Underlying stream exhausted. Breaking..."); 284: break; 285: } 286: datalen = data.length; 287: // copy [part of] the result to b 288: remaining = (datalen <= len) ? 0 : datalen - len; 289: delta = datalen - remaining; 290: System.arraycopy(data, 0, b, off, delta); 291: if (remaining > 0) 292: { 293: internalBuf = new byte[remaining]; 294: System.arraycopy(data, delta, internalBuf, 0, remaining); 295: } 296: // update off, result and len 297: off += delta; 298: result += delta; 299: len -= delta; 300: } 301: else 302: { // nothing much we can do except return what we have 303: if (Configuration.DEBUG) 304: log.finer("Not enough bytes in source to read a buffer. Breaking..."); 305: break; 306: } 307: } 308: if (Configuration.DEBUG) 309: { 310: log.finer("Remaining: " 311: + (internalBuf == null ? 0 : internalBuf.length)); 312: log.exiting(this.getClass().getName(), "read()", String.valueOf(result)); 313: } 314: return result; 315: } 316: 317: /** 318: * Reads a SASL buffer from the underlying source if at least 4 bytes are 319: * available. 320: * 321: * @return the byte[] of decoded buffer contents, or null if the underlying 322: * source was exhausted. 323: * @throws IOException if an I/O exception occurs during the operation. 324: */ 325: private byte[] readSaslBuffer() throws IOException 326: { 327: if (Configuration.DEBUG) 328: log.entering(this.getClass().getName(), "readSaslBuffer()"); 329: int realLength; // check if we read as many bytes as we're supposed to 330: byte[] result = new byte[4]; 331: try 332: { 333: realLength = source.read(result); 334: if (realLength == -1) 335: { 336: if (Configuration.DEBUG) 337: log.exiting(this.getClass().getName(), "readSaslBuffer"); 338: return null; 339: } 340: } 341: catch (IOException x) 342: { 343: if (Configuration.DEBUG) 344: log.throwing(this.getClass().getName(), "readSaslBuffer", x); 345: throw x; 346: } 347: if (realLength != 4) 348: throw new IOException("Was expecting 4 but found " + realLength); 349: int bufferLength = result[0] << 24 350: | (result[1] & 0xFF) << 16 351: | (result[2] & 0xFF) << 8 352: | (result[3] & 0xFF); 353: if (Configuration.DEBUG) 354: log.finer("SASL buffer size: " + bufferLength); 355: if (bufferLength > maxRawSendSize || bufferLength < 0) 356: throw new SaslEncodingException("SASL buffer (security layer) too long"); 357: 358: result = new byte[bufferLength]; 359: try 360: { 361: realLength = source.read(result); 362: } 363: catch (IOException x) 364: { 365: if (Configuration.DEBUG) 366: log.throwing(this.getClass().getName(), "readSaslBuffer", x); 367: throw x; 368: } 369: if (realLength != bufferLength) 370: throw new IOException("Was expecting " + bufferLength + " but found " 371: + realLength); 372: if (Configuration.DEBUG) 373: { 374: log.finer("Incoming buffer (before security) (hex): " 375: + Util.dumpString(result)); 376: log.finer("Incoming buffer (before security) (str): \"" 377: + new String(result) + "\""); 378: } 379: if (client != null) 380: result = client.unwrap(result, 0, realLength); 381: else 382: result = server.unwrap(result, 0, realLength); 383: if (Configuration.DEBUG) 384: { 385: log.finer("Incoming buffer (after security) (hex): " 386: + Util.dumpString(result)); 387: log.finer("Incoming buffer (after security) (str): \"" 388: + new String(result) + "\""); 389: log.exiting(this.getClass().getName(), "readSaslBuffer"); 390: } 391: return result; 392: } 393: }