1
/* SaslInputStream.java --
2
Copyright (C) 2003, 2006 Free Software Foundation, Inc.
4
This file is a part of GNU Classpath.
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.
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.
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
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
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. */
39
package gnu.javax.crypto.sasl;
41
import gnu.java.security.util.Util;
43
import java.io.InputStream;
44
import java.io.InterruptedIOException;
45
import java.io.IOException;
46
import java.io.PrintWriter;
48
import javax.security.sasl.Sasl;
49
import javax.security.sasl.SaslClient;
50
import javax.security.sasl.SaslServer;
53
* An input stream that uses either a {@link SaslClient} or a {@link SaslServer}
54
* to process the data through these entities' security layer filter(s).
56
public class SaslInputStream extends InputStream
59
// Debugging methods and variables
60
// -------------------------------------------------------------------------
62
private static final String NAME = "SaslOutputStream";
64
private static final String ERROR = "ERROR";
66
private static final String WARN = " WARN";
68
// private static final String INFO = " INFO";
69
private static final String TRACE = "DEBUG";
71
private static final boolean DEBUG = true;
73
private static final int debuglevel = 3;
75
private static final PrintWriter err = new PrintWriter(System.out, true);
77
private static void debug(String level, Object obj)
79
err.println("[" + level + "] " + NAME + ": " + String.valueOf(obj));
82
// Constants and variables
83
// -------------------------------------------------------------------------
85
private SaslClient client;
87
private SaslServer server;
89
private int maxRawSendSize;
91
private InputStream source;
93
private byte[] internalBuf;
96
// -------------------------------------------------------------------------
98
public SaslInputStream(SaslClient client, InputStream source)
103
this.client = client;
104
maxRawSendSize = Integer.parseInt((String) client.getNegotiatedProperty(Sasl.RAW_SEND_SIZE));
106
this.source = source;
109
public SaslInputStream(SaslServer server, InputStream source)
114
this.server = server;
115
maxRawSendSize = Integer.parseInt((String) server.getNegotiatedProperty(Sasl.RAW_SEND_SIZE));
117
this.source = source;
121
// -------------------------------------------------------------------------
124
// -------------------------------------------------------------------------
126
// Overloaded java.io.InputStream methods ----------------------------------
128
public int available() throws IOException
130
return (internalBuf == null) ? 0 : internalBuf.length;
133
public void close() throws IOException
139
* <p>Reads the next byte of data from the input stream. The value byte is
140
* returned as an <code>int</code> in the range <code>0</code> to
141
* <code>255</code>. If no byte is available because the end of the stream
142
* has been reached, the value <code>-1</code> is returned. This method
143
* blocks until input data is available, the end of the stream is detected,
144
* or an exception is thrown.</p>
146
* <p>From a SASL mechanism provider's perspective, if a security layer has
147
* been negotiated, the underlying <i>source</i> is expected to contain SASL
148
* buffers, as defined in RFC 2222. Four octets in network byte order in the
149
* front of each buffer identify the length of the buffer. The provider is
150
* responsible for performing any integrity checking or other processing on
151
* the buffer before returning the data as a stream of octets. For example,
152
* the protocol driver's request for a single octet from the stream might;
153
* i.e. an invocation of this method, may result in an entire SASL buffer
154
* being read and processed before that single octet can be returned.</p>
156
* @return the next byte of data, or <code>-1</code> if the end of the stream
158
* @throws IOException if an I/O error occurs.
160
public int read() throws IOException
163
if (internalBuf != null && internalBuf.length > 0)
165
result = internalBuf[0] & 0xFF;
166
if (internalBuf.length == 1)
167
internalBuf = new byte[0];
170
byte[] tmp = new byte[internalBuf.length - 1];
171
// System.arraycopy(internalBuf, 0, tmp, 0, tmp.length);
172
System.arraycopy(internalBuf, 1, tmp, 0, tmp.length);
178
byte[] buf = new byte[1];
179
int check = read(buf);
180
result = (check > 0) ? (buf[0] & 0xFF) : -1;
187
* <p>Reads up to <code>len</code> bytes of data from the underlying
188
* <i>source</i> input stream into an array of bytes. An attempt is made to
189
* read as many as <code>len</code> bytes, but a smaller number may be read,
190
* possibly zero. The number of bytes actually read is returned as an
193
* <p>This method blocks until input data is available, end of file is
194
* detected, or an exception is thrown.</p>
196
* <p>If <code>b</code> is <code>null</code>, a {@link NullPointerException} is
199
* <p>If <code>off</code> is negative, or <code>len</code> is negative, or
200
* <code>off+len</code> is greater than the length of the array <code>b</code>,
201
* then an {@link IndexOutOfBoundsException} is thrown.</p>
203
* <p>If <code>len</code> is zero, then no bytes are read and <code>0</code>
204
* is returned; otherwise, there is an attempt to read at least one byte. If
205
* no byte is available because the stream is at end of file, the value
206
* <code>-1</code> is returned; otherwise, at least one byte is read and
207
* stored into <code>b</code>.</p>
209
* <p>The first byte read is stored into element <code>b[off]</code>, the
210
* next one into <code>b[off+1]</code>, and so on. The number of bytes read
211
* is, at most, equal to <code>len</code>. Let <code>k</code> be the number
212
* of bytes actually read; these bytes will be stored in elements
213
* <code>b[off]</code> through <code>b[off+k-1]</code>, leaving elements
214
* <code>b[off+k]</code> through <code>b[off+len-1]</code> unaffected.</p>
216
* <p>In every case, elements <code>b[0]</code> through <code>b[off]</code>
217
* and elements <code>b[off+len]</code> through <code>b[b.length-1]</code>
218
* are unaffected.</p>
220
* <p>If the first byte cannot be read for any reason other than end of file,
221
* then an {@link IOException} is thrown. In particular, an {@link IOException}
222
* is thrown if the input stream has been closed.</p>
224
* <p>From the SASL mechanism provider's perspective, if a security layer has
225
* been negotiated, the underlying <i>source</i> is expected to contain SASL
226
* buffers, as defined in RFC 2222. Four octets in network byte order in the
227
* front of each buffer identify the length of the buffer. The provider is
228
* responsible for performing any integrity checking or other processing on
229
* the buffer before returning the data as a stream of octets. The protocol
230
* driver's request for a single octet from the stream might result in an
231
* entire SASL buffer being read and processed before that single octet can
234
* @param b the buffer into which the data is read.
235
* @param off the start offset in array <code>b</code> at which the data is
237
* @param len the maximum number of bytes to read.
238
* @return the total number of bytes read into the buffer, or <code>-1</code>
239
* if there is no more data because the end of the stream has been reached.
240
* @throws IOException if an I/O error occurs.
242
public int read(byte[] b, int off, int len) throws IOException
244
if (DEBUG && debuglevel > 8)
245
debug(TRACE, "==> read(b, " + String.valueOf(off) + ", "
246
+ String.valueOf(len) + ")");
250
throw new NullPointerException("b");
252
if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length)
253
|| ((off + len) < 0))
255
throw new IndexOutOfBoundsException("off=" + String.valueOf(off)
256
+ ", len=" + String.valueOf(len)
258
+ String.valueOf(b.length));
262
if (DEBUG && debuglevel > 8)
263
debug(TRACE, "<== read() --> 0");
267
if (DEBUG && debuglevel > 6)
268
debug(TRACE, "Available: " + String.valueOf(available()));
271
if (internalBuf == null || internalBuf.length < 1)
274
internalBuf = readSaslBuffer();
275
if (internalBuf == null)
277
if (DEBUG && debuglevel > 4)
278
debug(WARN, "Underlying stream empty. Returning -1");
279
if (DEBUG && debuglevel > 8)
280
debug(TRACE, "<== read() --> -1");
284
catch (InterruptedIOException x)
286
if (DEBUG && debuglevel > 6)
288
if (DEBUG && debuglevel > 4)
289
debug(WARN, "Reading thread was interrupted. Returning -1");
290
if (DEBUG && debuglevel > 8)
291
debug(TRACE, "<== read() --> -1");
295
if (len <= internalBuf.length)
298
System.arraycopy(internalBuf, 0, b, off, len);
299
if (len == internalBuf.length)
303
byte[] tmp = new byte[internalBuf.length - len];
304
System.arraycopy(internalBuf, len, tmp, 0, tmp.length);
310
// first copy the available bytes to b
311
result = internalBuf.length;
312
System.arraycopy(internalBuf, 0, b, off, result);
318
int remaining; // count of bytes remaining in buffer after an iteration
319
int delta; // count of bytes moved to b after an iteration
323
// we need to read SASL buffers, as long as there are at least
324
// 4 bytes available at the source
325
if (source.available() > 3)
328
data = readSaslBuffer();
331
if (DEBUG && debuglevel > 4)
332
debug(WARN, "Underlying stream exhausted. Breaking...");
336
datalen = data.length;
338
// copy [part of] the result to b
339
remaining = (datalen <= len) ? 0 : datalen - len;
340
delta = datalen - remaining;
341
System.arraycopy(data, 0, b, off, delta);
344
internalBuf = new byte[remaining];
345
System.arraycopy(data, delta, internalBuf, 0, remaining);
348
// update off, result and len
354
{ // nothing much we can do except return what we have
355
if (DEBUG && debuglevel > 4)
357
"Not enough bytes in source to read a buffer. Breaking...");
362
if (DEBUG && debuglevel > 6)
363
debug(TRACE, "Remaining: "
364
+ (internalBuf == null ? 0 : internalBuf.length));
365
if (DEBUG && debuglevel > 8)
366
debug(TRACE, "<== read() --> " + String.valueOf(result));
370
// other nstance methods ---------------------------------------------------
373
* Reads a SASL buffer from the underlying source if at least 4 bytes are
376
* @return the byte[] of decoded buffer contents, or null if the underlying
377
* source was exhausted.
378
* @throws IOException if an I/O exception occurs during the operation.
380
private byte[] readSaslBuffer() throws IOException
382
if (DEBUG && debuglevel > 8)
383
debug(TRACE, "==> readSaslBuffer()");
385
int realLength; // check if we read as many bytes as we're supposed to
386
byte[] result = new byte[4];
389
realLength = source.read(result);
390
if (realLength == -1)
392
if (DEBUG && debuglevel > 8)
393
debug(TRACE, "<== readSaslBuffer() --> null");
397
catch (IOException x)
399
if (DEBUG && debuglevel > 0)
406
throw new IOException("Was expecting 4 but found "
407
+ String.valueOf(realLength));
409
int bufferLength = result[0] << 24 | (result[1] & 0xFF) << 16
410
| (result[2] & 0xFF) << 8 | (result[3] & 0xFF);
412
if (DEBUG && debuglevel > 6)
413
debug(TRACE, "SASL buffer size: " + bufferLength);
414
if (bufferLength > maxRawSendSize || bufferLength < 0)
416
throw new SaslEncodingException("SASL buffer (security layer) too long");
419
result = new byte[bufferLength];
422
realLength = source.read(result);
424
catch (IOException x)
426
if (DEBUG && debuglevel > 0)
431
if (realLength != bufferLength)
432
throw new IOException("Was expecting " + String.valueOf(bufferLength)
433
+ " but found " + String.valueOf(realLength));
434
if (DEBUG && debuglevel > 6)
435
debug(TRACE, "Incoming buffer (before security) (hex): "
436
+ Util.dumpString(result));
437
if (DEBUG && debuglevel > 6)
438
debug(TRACE, "Incoming buffer (before security) (str): \""
439
+ new String(result) + "\"");
443
result = client.unwrap(result, 0, realLength);
447
result = server.unwrap(result, 0, realLength);
449
if (DEBUG && debuglevel > 6)
450
debug(TRACE, "Incoming buffer (after security) (hex): "
451
+ Util.dumpString(result));
452
if (DEBUG && debuglevel > 6)
453
debug(TRACE, "Incoming buffer (after security) (str): \""
454
+ new String(result) + "\"");
455
if (DEBUG && debuglevel > 8)
456
debug(TRACE, "<== readSaslBuffer()");
b'\\ No newline at end of file'