cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ssh2_wr.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib SSHv2 Session Write Routines *
4 * Copyright Peter Gutmann 1998-2008 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "misc_rw.h"
11  #include "session.h"
12  #include "ssh.h"
13 #else
14  #include "crypt.h"
15  #include "enc_dec/misc_rw.h"
16  #include "session/session.h"
17  #include "session/ssh.h"
18 #endif /* Compiler-specific includes */
19 
20 #ifdef USE_SSH
21 
22 /****************************************************************************
23 * *
24 * Sub-packet Management Routines *
25 * *
26 ****************************************************************************/
27 
28 /* Unlike SSL, SSH only hashes portions of the handshake, and even then not
29  complete packets but arbitrary bits and pieces. In order to handle this
30  we have to be able to break out bits and pieces of data from the stream
31  buffer in order to hash them. The following function extracts a block
32  of data from a given position in the stream buffer */
33 
34 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
35 int streamBookmarkComplete( INOUT STREAM *stream,
36  OUT_OPT_PTR void **dataPtrPtr,
37  OUT_LENGTH_Z int *length,
38  IN_LENGTH const int position )
39  {
40  const int dataLength = stell( stream ) - position;
41 
42  assert( isWritePtr( stream, sizeof( STREAM ) ) );
43  assert( isWritePtr( dataPtrPtr, sizeof( void * ) ) );
44  assert( isWritePtr( length, sizeof( int ) ) );
45 
46  REQUIRES( position >= 0 && position < MAX_INTLENGTH );
47  REQUIRES( dataLength > 0 || dataLength < stell( stream ) );
48 
49  /* Clear return values */
50  *dataPtrPtr = NULL;
51  *length = 0;
52 
53  *length = dataLength;
54  return( sMemGetDataBlockAbs( stream, position, dataPtrPtr, dataLength ) );
55  }
56 
57 /* Open a stream to write an SSH2 packet or continue an existing stream to
58  write further packets. This opens the stream (if it's an open), skips
59  the storage for the packet header, and writes the packet type */
60 
61 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
62 int openPacketStreamSSH( OUT STREAM *stream,
66  const int packetType )
67  {
68  assert( isWritePtr( stream, sizeof( STREAM ) ) );
69  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
70 
71  REQUIRES( packetType >= SSH_MSG_DISCONNECT && \
72  packetType <= SSH_MSG_CHANNEL_FAILURE );
73 
74  sMemOpen( stream, sessionInfoPtr->sendBuffer,
75  sessionInfoPtr->sendBufSize - EXTRA_PACKET_SIZE );
76  swrite( stream, "\x00\x00\x00\x00\x00", SSH2_HEADER_SIZE );
77  return( sputc( stream, packetType ) );
78  }
79 
80 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
81 int openPacketStreamSSHEx( OUT STREAM *stream,
82  const SESSION_INFO *sessionInfoPtr,
83  IN_LENGTH const int bufferSize,
86  const int packetType )
87  {
88  const int streamSize = bufferSize + SSH2_HEADER_SIZE;
89 
90  assert( isWritePtr( stream, sizeof( STREAM ) ) );
91  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
92  assert( isWritePtr( sessionInfoPtr->sendBuffer, streamSize ) );
93 
94  REQUIRES( bufferSize > 0 && bufferSize < MAX_INTLENGTH );
95  REQUIRES( packetType >= SSH_MSG_DISCONNECT && \
96  packetType <= SSH_MSG_CHANNEL_FAILURE );
97  REQUIRES( streamSize > SSH2_HEADER_SIZE && \
98  streamSize <= sessionInfoPtr->sendBufSize - EXTRA_PACKET_SIZE );
99 
100  sMemOpen( stream, sessionInfoPtr->sendBuffer, streamSize );
101  swrite( stream, "\x00\x00\x00\x00\x00", SSH2_HEADER_SIZE );
102  return( sputc( stream, packetType ) );
103  }
104 
105 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
106 int continuePacketStreamSSH( INOUT STREAM *stream,
109  const int packetType,
111  {
112  const int offset = stell( stream );
113  int status;
114 
115  assert( isWritePtr( stream, sizeof( STREAM ) ) );
116  assert( isWritePtr( packetOffset, sizeof( int ) ) );
117 
118  REQUIRES( packetType >= SSH_MSG_DISCONNECT && \
119  packetType <= SSH_MSG_CHANNEL_FAILURE );
120  REQUIRES( stell( stream ) == 0 || \
121  ( stell( stream ) > SSH2_HEADER_SIZE + 1 && \
122  stell( stream ) < MAX_INTLENGTH ) );
123 
124  /* Clear return value */
125  *packetOffset = 0;
126 
127  swrite( stream, "\x00\x00\x00\x00\x00", SSH2_HEADER_SIZE );
128  status = sputc( stream, packetType );
129  if( cryptStatusError( status ) )
130  return( status );
131  *packetOffset = offset;
132 
133  return( CRYPT_OK );
134  }
135 
136 /****************************************************************************
137 * *
138 * Write/Wrap a Packet *
139 * *
140 ****************************************************************************/
141 
142 /* Send an SSH packet. During the handshake phase we may be sending
143  multiple packets at once, however unlike SSL, SSH requires that each
144  packet in a multi-packet group be individually gift-wrapped so we have to
145  provide a facility for separately wrapping and sending packets to handle
146  this:
147 
148  sendBuffer bStartPtr eLen (may be zero)
149  | | |
150  v v |<-- payloadLen --->| <-+->
151  +-----------+---+-------------------+---+---+
152  |///////////|hdr| data |pad|MAC|
153  +-----------+---+-------------------+---+---+
154  ^ ^ |
155  | | |
156  offset stell(s)|
157  |<-------- length --------->| */
158 
159 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
160 int wrapPacketSSH2( INOUT SESSION_INFO *sessionInfoPtr,
161  INOUT STREAM *stream,
162  IN_LENGTH_Z const int offset,
164  const BOOLEAN isWriteableStream )
165  {
166  SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
167  int length = stell( stream ) - offset;
168  const int payloadLength = length - SSH2_HEADER_SIZE;
169  const int padBlockSize = max( sessionInfoPtr->cryptBlocksize, 8 );
170  void *bufStartPtr;
171  const int extraLength = \
172  ( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE ) ? \
173  sessionInfoPtr->authBlocksize : 0;
174  int padLength, status;
175 
176  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
177  assert( isWritePtr( stream, sizeof( STREAM ) ) );
178  assert( sStatusOK( stream ) );
179 
180  REQUIRES( offset >= 0 && offset < MAX_INTLENGTH );
181  REQUIRES( length >= SSH2_HEADER_SIZE && length < MAX_INTLENGTH );
182  REQUIRES( payloadLength >= 0 && payloadLength < length && \
183  offset + length + extraLength <= sessionInfoPtr->sendBufSize );
184 
185  /* Evaluate the number of padding bytes that we need to add to a packet
186  to make it a multiple of the cipher block size long, with a minimum
187  padding size of SSH2_MIN_PADLENGTH_SIZE bytes. Note that this padding
188  is required even when there's no encryption being applied(?), although
189  we set the padding to all zeroes in this case */
190  if( useQuantisedPadding )
191  {
192  /* It's something like a user-authentication packet that (probably)
193  contains a password, adjust the padding to make the overall
194  packet fixed-length to hide the password length information */
195  for( padLength = 256;
196  ( length + SSH2_MIN_PADLENGTH_SIZE ) > padLength;
197  padLength += 256 );
198  padLength -= length;
199  }
200  else
201  {
202  padLength = roundUp( length + SSH2_MIN_PADLENGTH_SIZE,
203  padBlockSize ) - length;
204  }
205  ENSURES( padLength >= SSH2_MIN_PADLENGTH_SIZE && padLength < 256 );
206  length += padLength;
207 
208  /* Make sure that there's enough room for the padding and MAC */
209  status = sMemGetDataBlockAbs( stream, offset, &bufStartPtr,
210  length + extraLength );
211  if( cryptStatusError( status ) )
212  {
213  DEBUG_DIAG(( "Not enough room for padding and MAC in data block" ));
214  assert( DEBUG_WARN );
215  return( CRYPT_ERROR_OVERFLOW );
216  }
217 
218  /* Add the SSH packet header, padding, and MAC:
219 
220  uint32 length (excluding MAC size)
221  byte padLen
222  [ byte[] data ]
223  byte[] padding
224  byte[] MAC */
225  if( isWriteableStream )
226  {
227  sseek( stream, offset );
228  writeUint32( stream, 1 + payloadLength + padLength );
229  status = sputc( stream, padLength );
230  if( cryptStatusOK( status ) )
231  status = sSkip( stream, payloadLength );
232  ENSURES( cryptStatusOK( status ) );
233  }
234  else
235  {
236  STREAM headerStream;
237 
238  /* If it's a non-writeable stream we have to insert the header data
239  directly into the stream buffer */
240  REQUIRES( offset == 0 && \
241  stell( stream ) == SSH2_HEADER_SIZE + payloadLength );
242  sMemOpen( &headerStream, bufStartPtr, SSH2_HEADER_SIZE );
243  writeUint32( &headerStream, 1 + payloadLength + padLength );
244  status = sputc( &headerStream, padLength );
245  sMemDisconnect( &headerStream );
246  ENSURES( cryptStatusOK( status ) );
247  }
248  DEBUG_PRINT(( "Wrote %s (%d) packet, length %d.\n",
249  getSSHPacketName( ( ( BYTE * ) bufStartPtr )[ LENGTH_SIZE + 1 ] ),
250  ( ( BYTE * ) bufStartPtr )[ LENGTH_SIZE + 1 ],
251  length - ( LENGTH_SIZE + 1 + ID_SIZE + padLength ) ));
252  DEBUG_DUMP_DATA( ( BYTE * ) bufStartPtr + LENGTH_SIZE + 1 + ID_SIZE,
253  length - ( LENGTH_SIZE + 1 + ID_SIZE + padLength ) );
254  if( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE )
255  {
257  BYTE padding[ 256 + 8 ];
258 
259  /* Append the padding */
260  setMessageData( &msgData, padding, padLength );
262  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
263  if( isWriteableStream )
264  status = swrite( stream, padding, padLength );
265  else
266  {
267  STREAM trailerStream;
268 
269  REQUIRES( stell( stream ) == length - padLength );
270  sMemOpen( &trailerStream,
271  ( BYTE * ) bufStartPtr + ( length - padLength ),
272  padLength );
273  status = swrite( &trailerStream, padding, padLength );
274  sMemDisconnect( &trailerStream );
275  if( cryptStatusOK( status ) )
276  status = sSkip( stream, padLength );
277  }
278  ENSURES( cryptStatusOK( status ) );
279 
280  /* MAC the data and append the MAC to the stream. We skip the
281  length value at the start since this is computed by the MAC'ing
282  code */
283  status = createMacSSH( sessionInfoPtr->iAuthOutContext,
284  sshInfo->writeSeqNo,
285  ( BYTE * ) bufStartPtr + LENGTH_SIZE,
286  length + extraLength - LENGTH_SIZE,
287  length - LENGTH_SIZE );
288  if( cryptStatusError( status ) )
289  return( status );
290  status = sSkip( stream, sessionInfoPtr->authBlocksize );
291  ENSURES( cryptStatusOK( status ) );
292 
293  /* Encrypt the entire packet except for the MAC */
294  status = krnlSendMessage( sessionInfoPtr->iCryptOutContext,
295  IMESSAGE_CTX_ENCRYPT, bufStartPtr,
296  length );
297  if( cryptStatusError( status ) )
298  return( status );
299  }
300  else
301  {
302  BYTE padding[ 256 + 8 ];
303 
304  /* If there's no security in effect yet then the padding is all
305  zeroes */
306  REQUIRES( isWriteableStream );
307  memset( padding, 0, padLength );
308  status = swrite( stream, padding, padLength );
309  ENSURES( cryptStatusOK( status ) );
310  }
311  sshInfo->writeSeqNo++;
312 
313  return( CRYPT_OK );
314  }
315 
316 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
317 int sendPacketSSH2( INOUT SESSION_INFO *sessionInfoPtr,
318  INOUT STREAM *stream,
319  const BOOLEAN sendOnly )
320  {
321  void *dataPtr;
322  int length, status;
323 
324  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
325  assert( isWritePtr( stream, sizeof( STREAM ) ) );
326 
327  /* If it's not a pre-assembled packet, wrap up the payload in an SSH
328  packet */
329  if( !sendOnly )
330  {
331  status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
332  if( cryptStatusError( status ) )
333  return( status );
334  }
335 
336  /* Send the contents of the stream to the peer */
337  length = stell( stream );
338  status = sMemGetDataBlockAbs( stream, 0, &dataPtr, length );
339  if( cryptStatusError( status ) )
340  return( status );
341  ANALYSER_HINT( dataPtr != NULL );
342  status = swrite( &sessionInfoPtr->stream, dataPtr, length );
343  if( cryptStatusError( status ) )
344  {
345  if( !( sessionInfoPtr->flags & SESSION_NOREPORTERROR ) )
346  sNetGetErrorInfo( &sessionInfoPtr->stream,
347  &sessionInfoPtr->errorInfo );
348  return( status );
349  }
350  return( CRYPT_OK ); /* swrite() returns a byte count */
351  }
352 #endif /* USE_SSH */