cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ssh2_msgs.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib SSHv2 Server-side Channel Message Management *
4 * Copyright Peter Gutmann 1998-2008 *
5 * *
6 ****************************************************************************/
7 
8 #include <stdio.h>
9 #if defined( INC_ALL )
10  #include "crypt.h"
11  #include "misc_rw.h"
12  #include "session.h"
13  #include "ssh.h"
14 #else
15  #include "crypt.h"
16  #include "enc_dec/misc_rw.h"
17  #include "session/session.h"
18  #include "session/ssh.h"
19 #endif /* Compiler-specific includes */
20 
21 #ifdef USE_SSH
22 
23 /* SSH identifies lower-level operations using awkward string-based
24  identifiers, to make these easier to work with we use lookup tables to
25  manage them and optionally map them to integer values */
26 
27 #ifdef USE_SSH_EXTENDED
28 typedef enum { REQUEST_NONE, REQUEST_SUBSYSTEM, REQUEST_SHELL, REQUEST_EXEC,
29  REQUEST_PORTFORWARD, REQUEST_PORTFORWARD_CANCEL, REQUEST_PTY,
30  REQUEST_NOOP, REQUEST_DISALLOWED } REQUEST_TYPE;
31 #else
32 typedef enum { REQUEST_NONE, REQUEST_SHELL, REQUEST_PTY, REQUEST_NOOP,
33  REQUEST_DISALLOWED } REQUEST_TYPE;
34 #endif /* USE_SSH_EXTENDED */
35 
36 #define REQUEST_FLAG_NONE 0x00/* No request flag */
37 #define REQUEST_FLAG_TERMINAL 0x01/* Request ends negotiation */
38 
39 typedef struct {
40  BUFFER_FIXED( requestNameLength ) \
41  const char FAR_BSS *requestName;/* String form of request type */
42  const int requestNameLength;
43  const REQUEST_TYPE requestType; /* Integer form of request type */
44  const int flags; /* Request flags */
45  } REQUEST_TYPE_INFO;
46 
47 typedef struct {
48  BUFFER_FIXED( channelNameLength ) \
49  const char FAR_BSS *channelName;/* String form of channel type */
50  const int channelNameLength;
51  const BOOLEAN isPortForwarding;
52  } CHANNEL_TYPE_INFO;
53 
54 /****************************************************************************
55 * *
56 * Utility Functions *
57 * *
58 ****************************************************************************/
59 
60 #ifdef USE_SSH_EXTENDED
61 
62 /* Read host name/address and port information and format it into string
63  form for the caller */
64 
65 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 5 ) ) \
66 static int readAddressAndPort( INOUT SESSION_INFO *sessionInfoPtr,
68  OUT_BUFFER( hostInfoMaxLen, *hostInfoLen ) \
69  char *hostInfo,
70  IN_LENGTH_SHORT_MIN( 16 ) const int hostInfoMaxLen,
71  OUT_LENGTH_SHORT_Z int *hostInfoLen )
72  {
73  BYTE stringBuffer[ CRYPT_MAX_TEXTSIZE + 8 ];
74  char portBuffer[ 16 + 8 ];
75  int stringLength, port, portLength, status;
76 
77  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
78  assert( isWritePtr( stream, sizeof( STREAM ) ) );
79  assert( isWritePtr( hostInfo, hostInfoMaxLen ) );
80  assert( isWritePtr( hostInfoLen, sizeof( int ) ) );
81 
82  REQUIRES( hostInfoMaxLen >= 16 && \
83  hostInfoMaxLen < MAX_INTLENGTH_SHORT );
84 
85  /* Clear return value */
86  memset( hostInfo, 0, hostInfoMaxLen );
87  *hostInfoLen = 0;
88 
89  /* Get the host and port:
90 
91  string host
92  uint32 port */
93  status = readString32( stream, stringBuffer, CRYPT_MAX_TEXTSIZE - 4,
94  &stringLength );
95  if( cryptStatusError( status ) || \
96  stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE - 4 )
97  {
100  "Invalid host name value" ) );
101  }
102  if( stringLength > hostInfoMaxLen )
103  {
104  /* Limit the returned size to the maximum requested by the caller */
105  stringLength = hostInfoMaxLen;
106  }
107  status = port = readUint32( stream );
108  if( cryptStatusError( status ) || port <= 0 || port >= 65535L )
109  {
112  "Invalid port number value" ) );
113  }
114 
115  /* Convert the information into string form for the caller to process.
116  Note that although we limit the maximum string length to 8 bytes the
117  buffer is actually declared as 16 bytes in size for those systems
118  that don't have sprintf_s() */
119  portLength = sprintf_s( portBuffer, 8, ":%d", port );
120  memcpy( hostInfo, stringBuffer, stringLength );
121  if( stringLength + portLength <= hostInfoMaxLen )
122  {
123  memcpy( hostInfo + stringLength, portBuffer, portLength );
124  stringLength += portLength;
125  }
126  *hostInfoLen = stringLength;
127 
128  return( CRYPT_OK );
129  }
130 
131 /* Add or clear host name/address and port information */
132 
133 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 5 ) ) \
134 static int getAddressAndPort( INOUT SESSION_INFO *sessionInfoPtr,
135  INOUT STREAM *stream,
136  OUT_BUFFER( hostInfoMaxLen, *hostInfoLen ) \
137  char *hostInfo,
138  IN_LENGTH_SHORT_MIN( 16 ) const int hostInfoMaxLen,
139  OUT_LENGTH_SHORT_Z int *hostInfoLen )
140  {
141  int status;
142 
143  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
144  assert( isWritePtr( stream, sizeof( STREAM ) ) );
145  assert( isWritePtr( hostInfo, hostInfoMaxLen ) );
146  assert( isWritePtr( hostInfoLen, sizeof( int ) ) );
147 
148  REQUIRES( hostInfoMaxLen >= 16 && \
149  hostInfoMaxLen < MAX_INTLENGTH_SHORT );
150 
151  /* Read the address and port information */
152  status = readAddressAndPort( sessionInfoPtr, stream, hostInfo,
153  hostInfoMaxLen, hostInfoLen );
154  if( cryptStatusError( status ) )
155  return( status );
156  if( getChannelStatusByAddr( sessionInfoPtr, hostInfo, \
157  *hostInfoLen ) != CHANNEL_NONE )
158  {
159  /* We're adding new forwarding information, if it already exists
160  this is an error */
163  "Received duplicate request for existing host/port %s",
164  sanitiseString( hostInfo, *hostInfoLen, *hostInfoLen ) ) );
165  }
166 
167  return( CRYPT_OK );
168  }
169 
170 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
171 static int clearAddressAndPort( INOUT SESSION_INFO *sessionInfoPtr,
172  INOUT STREAM *stream )
173  {
174  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
175  assert( isWritePtr( stream, sizeof( STREAM ) ) );
176 
177 #if 0 /* This is a somewhat special-case function in that it does't apply
178  to an open channel but to a past request for forwarding that
179  exists outside of the normal attribute space. Until this type of
180  functionality is explicitly requested by users we don't handle
181  this special-case non-attribute data setting */
182  SSH_CHANNEL_INFO *channelInfoPtr;
183  char hostInfo[ CRYPT_MAX_TEXTSIZE + 8 ];
184  int hostInfoLen, status;
185 
186  /* Read the address and port information */
187  status = readAddressAndPort( sessionInfoPtr, stream, hostInfo,
188  hostInfoMaxLen, &hostInfoLen );
189  if( cryptStatusError( status ) )
190  return( status );
191  return( deleteChannelAddr( sessionInfoPtr, addrInfo, addrInfoLen ) );
192 #else
193  return( CRYPT_OK );
194 #endif /* 0 */
195  }
196 #endif /* USE_SSH_EXTENDED */
197 
198 /* Send a response to a global/channel request */
199 
201 static int sendChannelResponse( INOUT SESSION_INFO *sessionInfoPtr,
202  IN const long channelNo,
203  const BOOLEAN isSuccessful )
204  {
205  int status;
206 
207  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
208 
209  REQUIRES( ( channelNo == CRYPT_USE_DEFAULT ) || \
210  ( channelNo >= 0 && channelNo <= LONG_MAX ) );
211 
212  /* Indicate that the request succeeded/was denied:
213 
214  byte type = SSH_MSG_CHANNEL/GLOBAL_SUCCESS/FAILURE
215  uint32 channel_no */
216  status = enqueueResponse( sessionInfoPtr,
217  isSuccessful ? SSH_MSG_CHANNEL_SUCCESS : \
219  ( channelNo == CRYPT_USE_DEFAULT ) ? \
220  getCurrentChannelNo( sessionInfoPtr, CHANNEL_READ ) : \
221  channelNo,
223  if( cryptStatusError( status ) )
224  return( status );
225  return( sendEnqueuedResponse( sessionInfoPtr ) );
226  }
227 
229 static int sendGlobalResponse( INOUT SESSION_INFO *sessionInfoPtr,
230  const BOOLEAN isSuccessful )
231  {
232  int status;
233 
234  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
235 
236  /* Indicate that the request succeeded/was denied:
237 
238  byte type = SSH_MSG_CHANNEL/GLOBAL_SUCCESS/FAILURE */
239  status = enqueueResponse( sessionInfoPtr,
240  isSuccessful ? SSH_MSG_GLOBAL_SUCCESS : \
243  CRYPT_UNUSED );
244  if( cryptStatusError( status ) )
245  return( status );
246  return( sendEnqueuedResponse( sessionInfoPtr ) );
247  }
248 
249 /****************************************************************************
250 * *
251 * Channel Open Management *
252 * *
253 ****************************************************************************/
254 
255 /* Process a channel open. These are server-only messages so in theory we
256  could immediately reject them in higher-level code if we're the client
257  but unfortunately we have to process at least some of the message in
258  order to obtain a channel number to use in the response */
259 
260 static const CHANNEL_TYPE_INFO FAR_BSS channelInfo[] = {
261  { "session", 7, FALSE },
262 #ifdef USE_SSH_EXTENDED
263  { "direct-tcpip", 12, TRUE },
264 #endif /* USE_SSH_EXTENDED */
265  { NULL, 0, FALSE }, { NULL, 0, FALSE }
266  };
267 
269 static int sendOpenResponseFailed( INOUT SESSION_INFO *sessionInfoPtr,
270  IN const long channelNo )
271  {
272  int status;
273 
274  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
275 
276  REQUIRES( channelNo >= 0 && channelNo <= LONG_MAX );
277 
278  /* Indicate that the request was denied:
279 
280  byte SSH_MSG_CHANNEL_OPEN_FAILURE
281  uint32 recipient_channel
282  uint32 reason_code = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED
283  string additional_text = ""
284  string language_tag = ""
285 
286  We always send the same reason code to avoid giving away anything
287  to an attacker */
288  status = enqueueResponse( sessionInfoPtr,
290  channelNo,
292  0, 0 );
293  if( cryptStatusOK( status ) )
294  status = sendEnqueuedResponse( sessionInfoPtr );
295  return( status );
296  }
297 
298 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
299 int processChannelOpen( INOUT SESSION_INFO *sessionInfoPtr,
300  INOUT STREAM *stream )
301  {
302  const CHANNEL_TYPE_INFO *channelInfoPtr = NULL;
303  BYTE typeString[ CRYPT_MAX_TEXTSIZE + 8 ];
304 #ifdef USE_SSH_EXTENDED
305  BYTE arg1String[ CRYPT_MAX_TEXTSIZE + 8 ];
306 #endif /* USE_SSH_EXTENDED */
307  BYTE *arg1Ptr = NULL;
308  long channelNo;
309  int typeLen, arg1Len = 0, maxPacketSize, i, status;
310 
311  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
312  assert( isWritePtr( stream, sizeof( STREAM ) ) );
313 
314  /* Read the channel open request (the type has already been read by the
315  caller):
316 
317  [ byte type = SSH_MSG_CHANNEL_OPEN ]
318  string channel_type = "session" | "direct-tcpip"
319  uint32 sender_channel
320  uint32 initial_window_size
321  uint32 max_packet_size
322  [ string host_to_connect - For port-forwarding
323  uint32 port_to_connect
324  string originator_IP_address
325  uint32 originator_port ]
326 
327  As with global/channel requests in processChannelOpen() we can't
328  return an error indication if we encounter a problem too early in the
329  packet, see the comment for that function for further details */
330  status = readString32( stream, typeString, CRYPT_MAX_TEXTSIZE,
331  &typeLen );
332  if( cryptStatusError( status ) || \
333  typeLen <= 0 || typeLen > CRYPT_MAX_TEXTSIZE )
334  {
337  "Invalid channel type in channel open message" ) );
338  }
339 
340  /* Try and identify the channel type */
341  for( i = 0; channelInfo[ i ].channelName != NULL && \
342  i < FAILSAFE_ARRAYSIZE( channelInfo, CHANNEL_TYPE_INFO );
343  i++ )
344  {
345  if( typeLen == channelInfo[ i ].channelNameLength && \
346  !memcmp( typeString, channelInfo[ i ].channelName,
347  typeLen ) )
348  {
349  channelInfoPtr = &channelInfo[ i ];
350  break;
351  }
352  }
353  ENSURES( i < FAILSAFE_ARRAYSIZE( channelInfo, CHANNEL_TYPE_INFO ) );
354  if( channelInfoPtr == NULL )
355  {
356  /* It's an unsupported channel open type, report it as an error */
359  "Invalid channel-open channel type '%s'",
360  sanitiseString( typeString, CRYPT_MAX_TEXTSIZE,
361  typeLen ) ) );
362  }
363  channelNo = readUint32( stream );
364  readUint32( stream ); /* Skip window size */
365  status = maxPacketSize = readUint32( stream );
366  if( cryptStatusError( status ) )
367  {
368  retExt( status,
369  ( status, SESSION_ERRINFO,
370  "Invalid '%s' channel parameters",
371  channelInfoPtr->channelName ) );
372  }
373  if( maxPacketSize < 1024 || maxPacketSize > 0x100000L )
374  {
375  /* General sanity check to make sure that the packet size is in the
376  range 1K ... 1MB. We've finally got valid packet data so we can
377  send error responses from now on */
378  ( void ) sendOpenResponseFailed( sessionInfoPtr, channelNo );
381  "Invalid '%s' channel maximum packet size parameter "
382  "value %d, should be 1K...1MB",
383  channelInfoPtr->channelName, maxPacketSize ) );
384  }
385 #ifdef USE_SSH_EXTENDED
386  if( channelInfoPtr->isPortForwarding )
387  {
388  /* Get the source and destination host information */
389  status = getAddressAndPort( sessionInfoPtr, stream,
390  arg1String, CRYPT_MAX_TEXTSIZE,
391  &arg1Len );
392  if( cryptStatusError( status ) )
393  {
394  ( void ) sendOpenResponseFailed( sessionInfoPtr, channelNo );
395  return( status );
396  }
397  arg1Ptr = arg1String;
398  }
399 #endif /* USE_SSH_EXTENDED */
400  maxPacketSize = min( maxPacketSize, \
401  sessionInfoPtr->receiveBufSize - EXTRA_PACKET_SIZE );
402 
403  /* If this is the client then opening a new channel by the server isn't
404  permitted */
405  if( !isServer( sessionInfoPtr ) )
406  {
407  ( void ) sendOpenResponseFailed( sessionInfoPtr, channelNo );
410  "Server attempted to a open a '%s' channel to the client",
411  channelInfoPtr->channelName ) );
412  }
413 
414  ENSURES( isServer( sessionInfoPtr ) );
415 
416  /* Add the new channel */
417  status = addChannel( sessionInfoPtr, channelNo, maxPacketSize,
418  typeString, typeLen, arg1Ptr, arg1Len );
419  if( cryptStatusError( status ) )
420  {
421  ( void ) sendOpenResponseFailed( sessionInfoPtr, channelNo );
422  retExt( status,
423  ( status, SESSION_ERRINFO,
424  "Couldn't add new '%s' channel %lX",
425  channelInfoPtr->channelName, channelNo ) );
426  }
427 
428  /* Send back the open confirmation:
429 
430  byte type = SSH_MSG_CHANNEL_OPEN_CONFIRMATION
431  uint32 recipient_channel = prev. sender_channel
432  uint32 sender_channel
433  uint32 initial_window_size = MAX_WINDOW_SIZE
434  uint32 max_packet_size = bufSize
435 
436  The SSH spec doesn't really explain the semantics of the server's
437  response to the channel open command, in particular whether the
438  returned data size parameters are merely a confirmation of the
439  client's requested values or whether the server is allowed to further
440  modify them to suit its own requirements (or perhaps one is for send
441  and the other for receive?). In the absence of any further guidance
442  we try and comply with a client's request for smaller data
443  quantities, but also return a smaller-than-requested data size value
444  if they ask for too much data.
445 
446  See the comments in the client-side channel-open code for the reason
447  for the window size */
448  status = enqueueResponse( sessionInfoPtr,
450  channelNo, channelNo,
451  getWindowSize( sessionInfoPtr ),
452  maxPacketSize );
453  if( cryptStatusOK( status ) )
454  status = sendEnqueuedResponse( sessionInfoPtr );
455  if( cryptStatusError( status ) )
456  {
457  /* Since we're already in an error state we can't do much more if
458  the cleanup from the failed operation fails */
459  ( void ) deleteChannel( sessionInfoPtr, channelNo, CHANNEL_BOTH,
460  TRUE );
461  return( status );
462  }
463 
464  /* The channel has been successfully created, mark it as active and
465  select it for future exchanges */
466  status = setChannelExtAttribute( sessionInfoPtr, SSH_ATTRIBUTE_ACTIVE,
467  TRUE );
468  if( cryptStatusOK( status ) )
469  {
470  const int windowSize = getWindowSize( sessionInfoPtr );
471 
472  /* The initial window count is the same as the data window size */
473  status = setChannelExtAttribute( sessionInfoPtr,
475  windowSize );
476  if( cryptStatusOK( status ) )
477  status = setChannelExtAttribute( sessionInfoPtr,
479  windowSize );
480  }
481  if( cryptStatusOK( status ) )
482  status = selectChannel( sessionInfoPtr, channelNo, CHANNEL_BOTH );
483  return( status );
484  }
485 
486 /****************************************************************************
487 * *
488 * Channel Request Management *
489 * *
490 ****************************************************************************/
491 
492 /* Process a global or channel request */
493 
494 static const REQUEST_TYPE_INFO FAR_BSS requestInfo[] = {
495  /* Channel/session-creation requests, only permitted on the server-side */
496  { "pty-req", 7, REQUEST_PTY, REQUEST_FLAG_NONE },
497  { "shell", 5, REQUEST_SHELL, REQUEST_FLAG_TERMINAL },
498 #ifdef USE_SSH_EXTENDED
499  { "subsystem", 9, REQUEST_SUBSYSTEM, REQUEST_FLAG_TERMINAL },
500  { "tcpip-forward", 13, REQUEST_PORTFORWARD, REQUEST_FLAG_NONE },
501  { "cancel-tcpip-forward", 20, REQUEST_PORTFORWARD_CANCEL, REQUEST_FLAG_NONE },
502  { "exec", 4, REQUEST_EXEC, REQUEST_FLAG_TERMINAL },
503 #endif /* USE_SSH_EXTENDED */
504 
505  /* No-op requests */
506  { "env", 3, REQUEST_NOOP, REQUEST_FLAG_NONE },
507  { "exit-signal", 11, REQUEST_NOOP, REQUEST_FLAG_NONE },
508  { "exit-status", 11, REQUEST_NOOP, REQUEST_FLAG_NONE },
509  { "signal", 6, REQUEST_NOOP, REQUEST_FLAG_NONE },
510  { "xon-xoff", 8, REQUEST_NOOP, REQUEST_FLAG_NONE },
511  { "window-change", 13, REQUEST_NOOP, REQUEST_FLAG_NONE },
512 
513  /* Disallowed requests */
514  { "x11-req", 7, REQUEST_DISALLOWED, REQUEST_FLAG_NONE },
515  { NULL, 0, REQUEST_NONE, REQUEST_FLAG_NONE },
516  { NULL, 0, REQUEST_NONE, REQUEST_FLAG_NONE }
517  };
518 
519 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
520 int processChannelRequest( INOUT SESSION_INFO *sessionInfoPtr,
521  INOUT STREAM *stream,
522  IN const long prevChannelNo )
523  {
524  SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
525  const REQUEST_TYPE_INFO *requestInfoPtr = NULL;
526  const BOOLEAN isChannelRequest = \
527  ( sshInfo->packetType == SSH_MSG_CHANNEL_REQUEST ) ? TRUE : FALSE;
528  BYTE stringBuffer[ CRYPT_MAX_TEXTSIZE + 8 ];
529  BOOLEAN wantReply, requestOK = TRUE;
530  int stringLength, i, status;
531 
532  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
533  assert( isWritePtr( stream, sizeof( STREAM ) ) );
534 
535  REQUIRES( prevChannelNo >= 0 && prevChannelNo <= LONG_MAX );
536 
537  /* Process the channel/global request (the type and channel number
538  have already been read by the caller):
539 
540  [ byte type = SSH_MSG_CHANNEL_REQUEST / SSH_MSG_GLOBAL_REQUEST ]
541  [ uint32 recipient_channel - For channel reqs ]
542  string request_type
543  boolean want_reply
544  [...]
545 
546  If there's an error at this point we can't send back a response
547  because one or both of the channel number and the want_reply flag
548  aren't available yet. The consensus among SSH implementors was that
549  not doing anything if the request packet is invalid is preferable to
550  sending back a response with a placeholder channel number or a
551  response when want_reply could have been false had it been able to
552  be decoded */
553  readString32( stream, stringBuffer, CRYPT_MAX_TEXTSIZE, &stringLength );
554  status = wantReply = sgetc( stream );
555  if( cryptStatusError( status ) || \
556  stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE )
557  {
560  "Invalid request type in %s request packet",
561  isChannelRequest ? "channel" : "global" ) );
562  }
563 
564  /* Try and identify the request type */
565  for( i = 0; requestInfo[ i ].requestName != NULL && \
566  i < FAILSAFE_ARRAYSIZE( requestInfo, REQUEST_TYPE_INFO );
567  i++ )
568  {
569  if( stringLength == requestInfo[ i ].requestNameLength && \
570  !memcmp( stringBuffer, requestInfo[ i ].requestName,
571  stringLength ) )
572  {
573  requestInfoPtr = &requestInfo[ i ];
574  break;
575  }
576  }
577  ENSURES( i < FAILSAFE_ARRAYSIZE( requestInfo, REQUEST_TYPE_INFO ) );
578 
579  /* If it's an explicitly disallowed request type or if we're the client
580  and it's anything other than a no-op request (for example a request
581  to execute a command or perform port forwarding) then it isn't
582  permitted */
583  if( requestInfoPtr == NULL || \
584  requestInfoPtr->requestType == REQUEST_DISALLOWED || \
585  ( !isServer( sessionInfoPtr ) && \
586  requestInfoPtr->requestType != REQUEST_NOOP ) )
587  {
588  /* If the other side doesn't want a response to their request, we're
589  done */
590  if( !wantReply )
591  return( CRYPT_OK );
592 
593  /* Send a request-denied response to the other side's request */
594  if( isChannelRequest )
595  {
596  int localStatus;
597 
598  /* The request failed, go back to the previous channel */
599  status = sendChannelResponse( sessionInfoPtr, prevChannelNo,
600  FALSE );
601  localStatus = selectChannel( sessionInfoPtr, prevChannelNo,
602  CHANNEL_READ );
603  if( cryptStatusOK( status ) )
604  status = localStatus;
605  }
606  else
607  status = sendGlobalResponse( sessionInfoPtr, FALSE );
608  return( status );
609  }
610 
611  ENSURES( requestInfoPtr != NULL && \
612  ( isServer( sessionInfoPtr ) || \
613  ( requestInfoPtr->requestType == REQUEST_NOOP ) ) );
614 
615  /* Process the request. Since these are administrative messages that
616  aren't visible to the caller we don't bail out if we encounter a
617  problem but just deny the request */
618  switch( requestInfoPtr->requestType )
619  {
620  case REQUEST_SHELL:
621  case REQUEST_PTY:
622  case REQUEST_NOOP:
623  /* Generic requests containing extra information that we're not
624  interested in */
625  break;
626 
627 #ifdef USE_SSH_EXTENDED
628  case REQUEST_EXEC:
629  /* A further generic request that we're not interested in */
630  break;
631 
632  case REQUEST_SUBSYSTEM:
633  /* We're being asked for a subsystem, record the type:
634 
635  [...]
636  string subsystem_name */
637  status = readString32( stream, stringBuffer, CRYPT_MAX_TEXTSIZE,
638  &stringLength );
639  if( cryptStatusError( status ) || \
640  stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE )
641  requestOK = FALSE;
642  else
643  {
644  /* The handling of subsystems is somewhat awkward, instead
645  of opening a subsystem channel SSH first opens a standard
646  session channel and then layers a subsystem on top of it.
647  Because of this we have to replace the standard channel
648  type with a new subsystem channel-type as well as recording
649  the subsystem type */
650  status = setChannelAttributeS( sessionInfoPtr,
652  "subsystem", 9 );
653  if( cryptStatusOK( status ) )
654  {
655  status = setChannelAttributeS( sessionInfoPtr,
657  stringBuffer,
658  stringLength );
659  }
660  ENSURES( cryptStatusOK( status ) );
661  }
662  break;
663 
664  case REQUEST_PORTFORWARD:
665  /* We're being asked for port forwarding, get the address and
666  port information:
667 
668  [...]
669  string local_address_to_bind (e.g. "0.0.0.0")
670  uint32 local_port_to_bind */
671  status = getAddressAndPort( sessionInfoPtr, stream, stringBuffer,
672  CRYPT_MAX_TEXTSIZE, &stringLength );
673  if( cryptStatusError( status ) )
674  requestOK = FALSE;
675  else
676  {
677 #if 0 /* This is a global request that doesn't apply to any
678  channel which makes it rather hard to deal with since
679  we can't associate it with anything that the user can
680  work with. For now we leave it until there's actual
681  user demand for it */
682  setChannelAttribute( sessionInfoPtr,
684  stringBuffer, stringLength );
685 #endif /* 0 */
686  }
687  break;
688 
689  case REQUEST_PORTFORWARD_CANCEL:
690  {
691  const int offset = stell( stream );
692  int iterationCount;
693 
694  /* Check that this is a request to close a port for which
695  forwarding was actually requested. Since there could be
696  multiple channels open on the forwarded port we keep looking
697  for other channels open on this port until we've cleared them
698  all. The spec is silent about what happens to open channels
699  when the forwarding is cancelled but from reading between the
700  lines (new channel-open requests can be received until the
701  forwarding is cancelled) it appears that the channels remain
702  active until the channel itself is closed */
703  for( requestOK = FALSE, iterationCount = 0;
704  iterationCount < FAILSAFE_ITERATIONS_MED;
705  iterationCount++ )
706  {
707  sseek( stream, offset );
708  status = clearAddressAndPort( sessionInfoPtr, stream );
709  if( cryptStatusError( status ) )
710  break;
711  requestOK = TRUE;
712  }
713  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
714  break;
715  }
716 #endif /* USE_SSH_EXTENDED */
717 
718  case REQUEST_DISALLOWED:
719  default:
720  /* Anything else we don't allow */
721  requestOK = FALSE;
722  break;
723  }
724 
725  /* Acknowledge the request if necessary */
726  if( wantReply )
727  {
728  if( isChannelRequest )
729  {
730  status = sendChannelResponse( sessionInfoPtr, prevChannelNo,
731  requestOK );
732  if( cryptStatusError( status ) || !requestOK )
733  {
734  /* The request failed, go back to the previous channel */
735  status = selectChannel( sessionInfoPtr, prevChannelNo,
736  CHANNEL_READ );
737  }
738  }
739  else
740  status = sendGlobalResponse( sessionInfoPtr, requestOK );
741  if( cryptStatusError( status ) )
742  return( status );
743  }
744 
745  /* If this request ends the negotiation, let the caller know */
746  return( ( requestInfoPtr->flags & REQUEST_FLAG_TERMINAL ) ? \
747  OK_SPECIAL : CRYPT_OK );
748  }
749 #endif /* USE_SSH */