cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ssh2_msgc.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib SSHv2 Client-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 /* The type of a channel-open request and the type of service that we're
24  requesting */
25 
26 typedef enum { OPENREQUEST_NONE, /* OPENREQUEST_STANDALONE, */
27  OPENREQUEST_CHANNELONLY, OPENREQUEST_SESSION,
28  OPENREQUEST_LAST } OPENREQUEST_TYPE;
29 
30 #ifdef USE_SSH_EXTENDED
31 typedef enum { SERVICE_NONE, SERVICE_SHELL, SERVICE_PORTFORWARD,
32  SERVICE_SUBSYSTEM, SERVICE_EXEC, SERVICE_LAST } SERVICE_TYPE;
33 #else
34 typedef enum { SERVICE_NONE, SERVICE_SHELL, SERVICE_LAST } SERVICE_TYPE;
35 #endif /* USE_SSH_EXTENDED */
36 
37 /****************************************************************************
38 * *
39 * Utility Functions *
40 * *
41 ****************************************************************************/
42 
43 #ifdef USE_SSH_EXTENDED
44 
45 /* Determine which type of service the caller requested */
46 
47 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
48 static int getServiceType( INOUT SESSION_INFO *sessionInfoPtr,
49  OUT_ENUM_OPT( SERVICE ) SERVICE_TYPE *serviceType )
50  {
51  BYTE typeString[ CRYPT_MAX_TEXTSIZE + 8 ];
52  int typeLen, status;
53 
54  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
55  assert( isWritePtr( serviceType, sizeof( SERVICE_TYPE ) ) );
56 
57  /* Clear return value */
58  *serviceType = SERVICE_NONE;
59 
60  /* Get the information that's needed for the channel that we're about
61  to create */
62  status = getChannelAttributeS( sessionInfoPtr,
64  typeString, CRYPT_MAX_TEXTSIZE,
65  &typeLen );
66  if( cryptStatusError( status ) )
67  {
68  retExt( status,
69  ( status, SESSION_ERRINFO,
70  "Missing channel type for channel activation" ) );
71  }
72  if( !strCompare( typeString, "subsystem", 9 ) )
73  {
74  *serviceType = SERVICE_SUBSYSTEM;
75  return( CRYPT_OK );
76  }
77  if( !strCompare( typeString, "direct-tcpip", 12 ) || \
78  !strCompare( typeString, "forwarded-tcpip", 15 ) )
79  {
80  *serviceType = SERVICE_PORTFORWARD;
81  return( CRYPT_OK );
82  }
83  if( !strCompare( typeString, "exec", 4 ) )
84  {
85  *serviceType = SERVICE_EXEC;
86  return( CRYPT_OK );
87  }
88 
89  /* The default is a just a straight pipe from A to B, a shell in SSH
90  thinking */
91  *serviceType = SERVICE_SHELL;
92  return( CRYPT_OK );
93  }
94 #else
95  #define getServiceType( sessionInfoPtr, serviceType ) \
96  CRYPT_OK; \
97  *( serviceType ) = SERVICE_SHELL
98 #endif /* USE_SSH_EXTENDED */
99 
100 /* Get information on why a channel-open failed */
101 
102 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
103 static int getOpenFailInfo( INOUT SESSION_INFO *sessionInfoPtr,
104  INOUT STREAM *stream )
105  {
106  BYTE stringBuffer[ CRYPT_MAX_TEXTSIZE + 8 ];
107  int stringLen, errorCode, status;
108 
109  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
110  assert( isWritePtr( stream, sizeof( STREAM ) ) );
111 
112  /* The channel open failed, tell the caller why:
113 
114  byte SSH_MSG_CHANNEL_OPEN_FAILURE
115  uint32 recipient_channel
116  uint32 reason_code
117  string additional_text */
118  readUint32( stream ); /* Skip channel number */
119  errorCode = readUint32( stream );
120  status = readString32( stream, stringBuffer, CRYPT_MAX_TEXTSIZE,
121  &stringLen );
122  if( cryptStatusError( status ) || \
123  stringLen <= 0 || stringLen > CRYPT_MAX_TEXTSIZE )
124  {
125  /* No error message, the best that we can do is give the reason code
126  (or the stream status if we couldn't even get that) as part of
127  the message */
130  "Channel open failed, reason code %d",
131  errorCode ) );
132  }
135  "Channel open failed, error message '%s'",
136  sanitiseString( stringBuffer, CRYPT_MAX_TEXTSIZE,
137  stringLen ) ) );
138  }
139 
140 /****************************************************************************
141 * *
142 * Client-side Channel Management *
143 * *
144 ****************************************************************************/
145 
146 /* Create a request for the appropriate type of service, either encrypted-
147  telnet, SFTP (or more generically a subsystem), or port forwarding.
148  There are several different port-forwarding mechanisms that we can use.
149  A global request of type "tcpip-forward" requests forwarding of a remote
150  port to the local system, specifying the remote port to be forwarded but
151  without actually opening a session/channel, it's merely a request for
152  future forwarding. When a connection arrives on the remote port for
153  which forwarding has been requested the remote system opens a channel of
154  type "forwarded-tcpip" to the local system. To open a connection from a
155  locally-forwarded port to a port on the remote system the local system
156  opens a channel of type "direct-tcpip" to the remote system:
157 
158  Pkt Name Arg1 Arg2 Comment
159  --- ---- ---- ---- -------
160  open "session" Followed by pty-req
161  or subsys
162  open "fwded-tcpip" remote_info (in) Server -> client in
163  response.to tcpip-fd
164  open "direct-tcpip" remote_info local_info Client -> server, currently
165  local_info = 127.0.0.1
166  global "tcpip-fwd" remote_info (out) Request for remote
167  forwarding
168 
169  Once we've opened a standard session we need to follow it with either a
170  pty-request + shell request or a subsystem request:
171 
172  Pkt Name Arg1 Arg2 Comment
173  --- ---- ---- ---- -------
174  channel "pty-req"
175  channel "subsystem" name
176 
177  In theory we could bundle the channel open + pty-request + shell request
178  into a single packet group to save round-trips but the packets sent after
179  the channel open require the use of the receive-channel number supplied by
180  the remote system. This is usually the same as the send channel that we
181  specify but for some unknown reason Cisco use different send and receive
182  channel numbers, requiring that we wait for the response to the channel-
183  open before we send any subsequent packets, which adds yet another RTT to
184  the exchange */
185 
186 #ifdef USE_SSH_EXTENDED
187 
188 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
189 static int createOpenRequest( INOUT SESSION_INFO *sessionInfoPtr,
190  OUT STREAM *stream,
191  IN_ENUM( SERVICE ) const SERVICE_TYPE serviceType,
192  OUT_ENUM_OPT( OPENREQUEST ) \
193  OPENREQUEST_TYPE *requestType )
194  {
195  const long channelNo = getCurrentChannelNo( sessionInfoPtr,
196  CHANNEL_WRITE );
197  const int maxPacketSize = sessionInfoPtr->sendBufSize - \
198  EXTRA_PACKET_SIZE;
199  URL_INFO urlInfo = { DUMMY_INIT };
200  BYTE arg1String[ CRYPT_MAX_TEXTSIZE + 8 ];
201  int arg1Len = DUMMY_INIT, status;
202 
203  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
204  assert( isWritePtr( stream, sizeof( STREAM ) ) );
205  assert( isWritePtr( requestType, sizeof( OPENREQUEST_TYPE ) ) );
206 
207  REQUIRES( serviceType > SERVICE_NONE && serviceType < SERVICE_LAST );
208 
209  /* Clear return value */
210  *requestType = OPENREQUEST_NONE;
211 
212  /* If it's not a generic tunnel, get any additional parameters
213  required */
214  if( serviceType != SERVICE_SHELL )
215  {
216  status = getChannelAttributeS( sessionInfoPtr,
218  arg1String, CRYPT_MAX_TEXTSIZE,
219  &arg1Len );
220  if( cryptStatusError( status ) )
221  {
222  retExt( status,
223  ( status, SESSION_ERRINFO,
224  "Missing channel argument (%s) for channel "
225  "activation",
226  ( serviceType == SERVICE_PORTFORWARD ) ? \
227  "host name/port" : \
228  ( serviceType == SERVICE_EXEC ) ? \
229  "command" : \
230  "subsystem name" ) );
231  }
232  }
233 
234  /* If we know that the argument is a URL (rather than a subsystem name
235  or command), check its validity */
236  if( serviceType == SERVICE_PORTFORWARD )
237  {
238  status = sNetParseURL( &urlInfo, arg1String, arg1Len, URL_TYPE_SSH );
239  if( cryptStatusError( status ) )
240  {
241  retExt( status,
242  ( status, SESSION_ERRINFO,
243  "Invalid channel argument (host name/port) for "
244  "channel activation" ) );
245  }
246 
247  /* Tell the caller what to do after the channel open */
248  *requestType = OPENREQUEST_CHANNELONLY;
249  }
250  else
251  {
252  /* Set the request type to tell the caller what to do after they've
253  sent the initial channel open */
254  *requestType = OPENREQUEST_SESSION;
255  }
256 
257 #if 0 /* 17/9/04 This is a complex mechanism that requires the use of an
258  interactive or scriptable tool to use, until someone
259  really needs this we don't implement it */
260  /* Request forwarding of a port from the remote system to the local one.
261  Once a connection arrives on the remote port it'll open a channel to
262  the local system of type "forwarded-tcpip". Since this isn't a
263  normal channel open, we return a special status to let the caller
264  know that there's nothing further to do */
265  if( serviceType == SERVICE_PORTFORWARD_REQUEST )
266  {
267  URL_INFO urlInfo;
268 
269  *requestType = OPENREQUEST_STANDALONE;
270 
271  /* byte type = SSH_MSG_GLOBAL_REQUEST
272  string request_name = "tcpip-forward"
273  boolean want_reply = FALSE
274  string remote_address_to_bind (e.g. "0.0.0.0")
275  uint32 remote_port_to_bind
276 
277  Since this is a special-case request-only message we let the
278  caller know that they don't have to proceed further with the
279  channel-open */
280  status = openPacketStreamSSH( stream, SSH_MSG_GLOBAL_REQUEST,
281  &packetOffset );
282  if( cryptStatusError( status ) )
283  return( status );
284  writeString32( stream, "tcpip-forward", 13 );
285  sputc( stream, 0 );
286  writeString32( stream, urlInfo.host, urlInfo.hostLen );
287  writeUint32( stream, urlInfo.port );
288  return( wrapPacketSSH2( sessionInfoPtr, stream, packetOffset ) );
289  }
290 #endif /* 0 */
291 
292  /* Send a channel open:
293 
294  byte type = SSH_MSG_CHANNEL_OPEN
295  string channel_type
296  uint32 sender_channel
297  uint32 initial_window_size = MAX_WINDOW_SIZE
298  uint32 max_packet_size = bufSize
299  ...
300 
301  The use of security protocol-level flow control when there's already
302  a far better, heavily analysed and field-tested network protocol-
303  level flow control mechanism present is just stupid. All it does is
304  create a performance handbrake where throughput can be reduced by as
305  much as an order of magnitude due to SSH's "flow-control" getting in
306  the way (Putty even has an FAQ entry "Why is SFTP so much slower than
307  scp?", for which the correct answer should be "It's the SSH-level
308  flow-control braindamage"). For this reason cryptlib always
309  advertises a maximum window size (effectively disabling the SSH-level
310  flow control) and lets the network stack and network hardware take
311  care of flow control, as they should. Unfortunately some buggy
312  implementations break when sent a window size over a certain limit
313  in which case we have to limit the window size, thus reintroducing
314  the performance handbrake when dealing with these buggy
315  implementations, see the comments for the window handling in
316  ssh2_msg.c for details */
317  status = openPacketStreamSSH( stream, sessionInfoPtr,
319  if( cryptStatusError( status ) )
320  return( status );
321  if( serviceType == SERVICE_SUBSYSTEM || serviceType == SERVICE_EXEC )
322  {
323  /* A subsystem is an additional layer on top of the standard
324  channel so we have to open the channel first and then add the
325  subsystem later via a channel request rather than opening it
326  directly. An exec is a special case that works like the default
327  type of session operation, "shell", but doesn't go via a pty */
328  writeString32( stream, "session", 7 );
329  }
330  else
331  {
332  if( serviceType == SERVICE_PORTFORWARD )
333  writeString32( stream, "direct-tcpip", 12 );
334  else
335  {
336  ENSURES( serviceType == SERVICE_SHELL );
337 
338  /* It's a generic secure-tunnel that'll be followed by a pty-
339  request and shell */
340  writeString32( stream, "session", 7 );
341  }
342  }
343  writeUint32( stream, channelNo );
344  writeUint32( stream, getWindowSize( sessionInfoPtr ) );
345  status = writeUint32( stream, maxPacketSize );
346  if( serviceType == SERVICE_PORTFORWARD )
347  {
348  /* The caller has requested a port-forwarding channel open, continue
349  the basic channel-open packet with port-forwarding information:
350 
351  ...
352  string remote_host_to_connect
353  uint32 rempte_port_to_connect
354  string local_originator_IP_address
355  uint32 local_originator_port */
356  writeString32( stream, urlInfo.host, urlInfo.hostLen );
357  writeUint32( stream, urlInfo.port );
358  writeString32( stream, "127.0.0.1", 9 );
359  status = writeUint32( stream, 22 );
360  }
361  if( cryptStatusOK( status ) )
362  status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
363  if( cryptStatusError( status ) )
364  {
365  sMemDisconnect( stream );
366  return( status );
367  }
368  return( CRYPT_OK );
369  }
370 
371 #else
372 
373 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
374 static int createOpenRequest( INOUT SESSION_INFO *sessionInfoPtr,
375  OUT STREAM *stream,
376  IN_ENUM( SERVICE ) const SERVICE_TYPE serviceType,
377  OUT_ENUM_OPT( OPENREQUEST ) \
378  OPENREQUEST_TYPE *requestType )
379  {
380  const long channelNo = getCurrentChannelNo( sessionInfoPtr,
381  CHANNEL_WRITE );
382  const int maxPacketSize = sessionInfoPtr->sendBufSize - \
383  EXTRA_PACKET_SIZE;
384  int status;
385 
386  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
387  assert( isWritePtr( stream, sizeof( STREAM ) ) );
388  assert( isWritePtr( requestType, sizeof( OPENREQUEST_TYPE ) ) );
389 
390  REQUIRES( serviceType > SERVICE_NONE && serviceType < SERVICE_LAST );
391 
392  /* Set the request type to tell the caller what to do after they've sent
393  the initial channel open */
394  *requestType = OPENREQUEST_SESSION;
395 
396  /* Send a channel open:
397 
398  byte type = SSH_MSG_CHANNEL_OPEN
399  string channel_type
400  uint32 sender_channel
401  uint32 initial_window_size = MAX_WINDOW_SIZE
402  uint32 max_packet_size = bufSize
403  ...
404 
405  The use of security protocol-level flow control when there's already
406  a far better, heavily analysed and field-tested network protocol-
407  level flow control mechanism present is just stupid. All it does is
408  create a performance handbrake where throughput can be reduced by as
409  much as an order of magnitude due to SSH's "flow-control" getting in
410  the way (Putty even has an FAQ entry "Why is SFTP so much slower than
411  scp?", for which the correct answer should be "It's the SSH-level
412  flow-control braindamage"). For this reason cryptlib always
413  advertises a maximum window size (effectively disabling the SSH-level
414  flow control) and lets the network stack and network hardware take
415  care of flow control, as they should. Unfortunately some buggy
416  implementations break when sent a window size over a certain limit
417  in which case we have to limit the window size, thus reintroducing
418  the performance handbrake when dealing with these buggy
419  implementations, see the comments for the window handling in
420  ssh2_msg.c for details */
421  status = openPacketStreamSSH( stream, sessionInfoPtr,
423  if( cryptStatusError( status ) )
424  return( status );
425  writeString32( stream, "session", 7 );
426  writeUint32( stream, channelNo );
427  writeUint32( stream, getWindowSize( sessionInfoPtr ) );
428  status = writeUint32( stream, maxPacketSize );
429  if( cryptStatusOK( status ) )
430  status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
431  if( cryptStatusError( status ) )
432  {
433  sMemDisconnect( stream );
434  return( status );
435  }
436  return( CRYPT_OK );
437  }
438 #endif /* USE_SSH_EXTENDED */
439 
440 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
441 static int createSessionOpenRequest( INOUT SESSION_INFO *sessionInfoPtr,
442  INOUT STREAM *stream,
443  IN_ENUM( SERVICE ) \
444  const SERVICE_TYPE serviceType )
445  {
446  const long channelNo = getCurrentChannelNo( sessionInfoPtr,
447  CHANNEL_WRITE );
448  int packetOffset, status;
449 
450  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
451  assert( isWritePtr( stream, sizeof( STREAM ) ) );
452 
453  REQUIRES( serviceType > SERVICE_NONE && serviceType < SERVICE_LAST );
454 
455 #ifdef USE_SSH_EXTENDED
456  /* If the caller has requested the use of a custom subsystem (and at the
457  moment the only one that's likely to be used is SFTP), request this
458  from the server by modifying the channel that we've just opened to
459  run the subsystem */
460  if( serviceType == SERVICE_SUBSYSTEM )
461  {
462  BYTE arg1String[ CRYPT_MAX_TEXTSIZE + 8 ];
463  int arg1Len;
464 
465  /* Get the subsystem type */
466  status = getChannelAttributeS( sessionInfoPtr,
468  arg1String, CRYPT_MAX_TEXTSIZE,
469  &arg1Len );
470  if( cryptStatusError( status ) )
471  return( status );
472 
473  /* byte type = SSH_MSG_CHANNEL_REQUEST
474  uint32 recipient_channel
475  string request_name = "subsystem"
476  boolean want_reply = FALSE
477  string subsystem_name */
478  status = openPacketStreamSSH( stream, sessionInfoPtr,
480  if( cryptStatusError( status ) )
481  return( status );
482  writeUint32( stream, channelNo );
483  writeString32( stream, "subsystem", 9 );
484  sputc( stream, 0 );
485  status = writeString32( stream, arg1String, arg1Len );
486  if( cryptStatusOK( status ) )
487  status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
488  return( status );
489  }
490 
491  /* If the caller has requested the use of remote command execution (i.e.
492  an rexec rather than the usual SSH rsh), run the command directly
493  without going via a pty */
494  if( serviceType == SERVICE_EXEC )
495  {
496  BYTE arg1String[ CRYPT_MAX_TEXTSIZE + 8 ];
497  int arg1Len;
498 
499  /* Get the command to execute */
500  status = getChannelAttributeS( sessionInfoPtr,
502  arg1String, CRYPT_MAX_TEXTSIZE,
503  &arg1Len );
504  if( cryptStatusError( status ) )
505  return( status );
506 
507  /* byte type = SSH_MSG_CHANNEL_REQUEST
508  uint32 recipient_channel
509  string request_name = "exec"
510  boolean want_reply = FALSE
511  string command */
512  status = openPacketStreamSSH( stream, sessionInfoPtr,
514  if( cryptStatusError( status ) )
515  return( status );
516  writeUint32( stream, channelNo );
517  writeString32( stream, "exec", 4 );
518  sputc( stream, 0 );
519  status = writeString32( stream, arg1String, arg1Len );
520  if( cryptStatusOK( status ) )
521  status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
522  return( status );
523  }
524 #endif /* USE_SSH_EXTENDED */
525 
526  REQUIRES( serviceType == SERVICE_SHELL );
527 
528  /* It's a standard channel open:
529 
530  byte type = SSH_MSG_CHANNEL_REQUEST
531  uint32 recipient_channel
532  string request_name = "pty-req"
533  boolean want_reply = FALSE
534  string TERM_environment_variable = "xterm"
535  uint32 cols = 80
536  uint32 rows = 48
537  uint32 pixel_width = 0
538  uint32 pixel_height = 0
539  string tty_mode_info = ""
540  ... */
541  status = openPacketStreamSSH( stream, sessionInfoPtr,
543  if( cryptStatusError( status ) )
544  return( status );
545  writeUint32( stream, channelNo );
546  writeString32( stream, "pty-req", 7 );
547  sputc( stream, 0 ); /* No reply */
548  writeString32( stream, "xterm", 5 );/* Generic */
549  writeUint32( stream, 80 );
550  writeUint32( stream, 48 ); /* 48 x 80 (24 x 80 is so 1970s) */
551  writeUint32( stream, 0 );
552  writeUint32( stream, 0 ); /* No graphics capabilities */
553  status = writeUint32( stream, 0 ); /* No special TTY modes */
554  if( cryptStatusOK( status ) )
555  status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
556  if( cryptStatusError( status ) )
557  return( status );
558 
559  /* ...
560  byte type = SSH_MSG_CHANNEL_REQUEST
561  uint32 recipient_channel
562  string request_name = "shell"
563  boolean want_reply = FALSE
564 
565  This final request, once sent, moves the server into interactive
566  session mode */
567  status = continuePacketStreamSSH( stream, SSH_MSG_CHANNEL_REQUEST,
568  &packetOffset );
569  if( cryptStatusError( status ) )
570  return( status );
571  writeUint32( stream, channelNo );
572  writeString32( stream, "shell", 5 );
573  status = sputc( stream, 0 ); /* No reply */
574  if( cryptStatusOK( status ) )
575  status = wrapPacketSSH2( sessionInfoPtr, stream, packetOffset,
576  FALSE, TRUE );
577  return( status );
578  }
579 
580 /* Send a channel open */
581 
583 int sendChannelOpen( INOUT SESSION_INFO *sessionInfoPtr )
584  {
585  STREAM stream;
586  SERVICE_TYPE serviceType;
587  OPENREQUEST_TYPE requestType;
588  const long channelNo = getCurrentChannelNo( sessionInfoPtr,
589  CHANNEL_READ );
590  long currentChannelNo;
591  int length, value, status;
592 
593  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
594 
595  /* Make sure that there's channel data available to activate and
596  that it doesn't correspond to an already-active channel */
597  if( channelNo == UNUSED_CHANNEL_NO )
598  {
601  "No current channel information available to activate "
602  "channel" ) );
603  }
604  status = getChannelAttribute( sessionInfoPtr,
606  &value );
607  if( cryptStatusError( status ) || value )
608  {
611  "Current channel has already been activated" ) );
612  }
613 
614  /* Determine the service type that we'll be using */
615  status = getServiceType( sessionInfoPtr, &serviceType );
616  if( cryptStatusError( status ) )
617  return( status );
618 
619  /* Create a request for the appropriate type of service */
620  status = createOpenRequest( sessionInfoPtr, &stream, serviceType,
621  &requestType );
622  if( cryptStatusError( status ) )
623  return( status );
624 
625 #if 0 /* Never used, see comment in createOpenRequest() for
626  "tcpip-forward" type */
627  /* If it's a request-only message that doesn't open a channel, send it
628  and exit */
629  if( requestType == OPENREQUEST_STANDALONE )
630  {
631  status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
632  sMemDisconnect( &stream );
633  return( status );
634  }
635 #endif /* 0 */
636 
637  /* Send the open request to the server. The SSH spec doesn't really
638  explain the semantics of the server's response to the channel open
639  command, in particular whether the returned data size parameters are
640  merely a confirmation of the client's requested values or whether the
641  server is allowed to further modify them to suit its own requirements
642  (or perhaps one is for send and the other for receive?). In the
643  absence of any further guidance we just ignore the returned values,
644  which seems to work for all deployed servers */
645  status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
646  sMemDisconnect( &stream );
647  if( cryptStatusError( status ) )
648  return( status );
649 
650  /* Wait for the server's ack of the channel open request:
651 
652  byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION
653  uint32 recipient_channel
654  uint32 sender_channel
655  uint32 initial_window_size
656  uint32 maximum_packet_size
657  ... */
658  status = length = \
659  readHSPacketSSH2( sessionInfoPtr, SSH_MSG_SPECIAL_CHANNEL,
662  if( cryptStatusError( status ) )
663  return( status );
664  sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
665  if( sessionInfoPtr->sessionSSH->packetType == SSH_MSG_CHANNEL_OPEN_FAILURE )
666  {
667  /* The open failed, report the details to the user */
668  status = getOpenFailInfo( sessionInfoPtr, &stream );
669  sMemDisconnect( &stream );
670  return( status );
671  }
672  currentChannelNo = readUint32( &stream );
673  if( currentChannelNo != channelNo )
674  {
675  sMemDisconnect( &stream );
678  "Invalid channel number %lX in channel open confirmation, "
679  "should be %lX", currentChannelNo, channelNo ) );
680  }
681  status = currentChannelNo = readUint32( &stream );
682  sMemDisconnect( &stream );
683  if( cryptStatusError( status ) )
684  {
687  "Invalid channel data in channel open confirmation for "
688  "channel %lX", channelNo ) );
689  }
690 
691  /* The channel has been successfully created, mark it as active and
692  select it for future exchanges */
693  status = setChannelExtAttribute( sessionInfoPtr, SSH_ATTRIBUTE_ACTIVE,
694  TRUE );
695  if( cryptStatusOK( status ) && currentChannelNo != channelNo )
696  {
697  /* It's unclear why anyone would want to use different channel
698  numbers for different directions since it's the same channel that
699  the data is moving across, but Cisco do it anyway */
700  status = setChannelExtAttribute( sessionInfoPtr,
702  currentChannelNo );
703  }
704  if( cryptStatusOK( status ) )
705  {
706  const int windowSize = getWindowSize( sessionInfoPtr );
707 
708  /* The initial window count is the same as the data window size */
709  status = setChannelExtAttribute( sessionInfoPtr,
711  windowSize );
712  if( cryptStatusOK( status ) )
713  status = setChannelExtAttribute( sessionInfoPtr,
715  windowSize );
716  }
717  if( cryptStatusOK( status ) )
718  status = selectChannel( sessionInfoPtr, channelNo, CHANNEL_BOTH );
719  if( cryptStatusError( status ) )
720  return( status );
721  if( requestType == OPENREQUEST_CHANNELONLY )
722  {
723  /* If we're just opening a new channel in an existing session, we're
724  done */
725  return( CRYPT_OK );
726  }
727  REQUIRES( requestType == OPENREQUEST_SESSION );
728 
729  /* It's a session open request that requires additional messages to do
730  anything useful, create and send the extra packets */
731  status = createSessionOpenRequest( sessionInfoPtr, &stream,
732  serviceType );
733  if( cryptStatusOK( status ) )
734  status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
735  sMemDisconnect( &stream );
736  return( status );
737  }
738 #endif /* USE_SSH */