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 ) ) \
getServiceType(INOUT SESSION_INFO * sessionInfoPtr,OUT_ENUM_OPT (SERVICE)SERVICE_TYPE * serviceType)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,
63 CRYPT_SESSINFO_SSH_CHANNEL_TYPE,
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 ) ) \
getOpenFailInfo(INOUT SESSION_INFO * sessionInfoPtr,INOUT STREAM * stream)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 */
128 retExt( CRYPT_ERROR_OPEN,
129 ( CRYPT_ERROR_OPEN, SESSION_ERRINFO,
130 "Channel open failed, reason code %d",
131 errorCode ) );
132 }
133 retExt( CRYPT_ERROR_OPEN,
134 ( CRYPT_ERROR_OPEN, SESSION_ERRINFO,
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 ) ) \
createOpenRequest(INOUT SESSION_INFO * sessionInfoPtr,OUT STREAM * stream,IN_ENUM (SERVICE)const SERVICE_TYPE serviceType,OUT_ENUM_OPT (OPENREQUEST)OPENREQUEST_TYPE * requestType)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_STRUCT;
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 values */
210 memset( stream, 0, sizeof( STREAM ) );
211 *requestType = OPENREQUEST_NONE;
212
213 /* If it's not a generic tunnel, get any additional parameters
214 required */
215 if( serviceType != SERVICE_SHELL )
216 {
217 status = getChannelAttributeS( sessionInfoPtr,
218 CRYPT_SESSINFO_SSH_CHANNEL_ARG1,
219 arg1String, CRYPT_MAX_TEXTSIZE,
220 &arg1Len );
221 if( cryptStatusError( status ) )
222 {
223 retExt( status,
224 ( status, SESSION_ERRINFO,
225 "Missing channel argument (%s) for channel "
226 "activation",
227 ( serviceType == SERVICE_PORTFORWARD ) ? \
228 "host name/port" : \
229 ( serviceType == SERVICE_EXEC ) ? \
230 "command" : \
231 "subsystem name" ) );
232 }
233 }
234
235 /* If we know that the argument is a URL (rather than a subsystem name
236 or command), check its validity */
237 if( serviceType == SERVICE_PORTFORWARD )
238 {
239 status = sNetParseURL( &urlInfo, arg1String, arg1Len, URL_TYPE_SSH );
240 if( cryptStatusError( status ) )
241 {
242 retExt( status,
243 ( status, SESSION_ERRINFO,
244 "Invalid channel argument (host name/port) for "
245 "channel activation" ) );
246 }
247
248 /* Tell the caller what to do after the channel open */
249 *requestType = OPENREQUEST_CHANNELONLY;
250 }
251 else
252 {
253 /* Set the request type to tell the caller what to do after they've
254 sent the initial channel open */
255 *requestType = OPENREQUEST_SESSION;
256 }
257
258 #if 0 /* 17/9/04 This is a complex mechanism that requires the use of an
259 interactive or scriptable tool to use, until someone
260 really needs this we don't implement it */
261 /* Request forwarding of a port from the remote system to the local one.
262 Once a connection arrives on the remote port it'll open a channel to
263 the local system of type "forwarded-tcpip". Since this isn't a
264 normal channel open, we return a special status to let the caller
265 know that there's nothing further to do */
266 if( serviceType == SERVICE_PORTFORWARD_REQUEST )
267 {
268 URL_INFO urlInfo;
269
270 *requestType = OPENREQUEST_STANDALONE;
271
272 /* byte type = SSH_MSG_GLOBAL_REQUEST
273 string request_name = "tcpip-forward"
274 boolean want_reply = FALSE
275 string remote_address_to_bind (e.g. "0.0.0.0")
276 uint32 remote_port_to_bind
277
278 Since this is a special-case request-only message we let the
279 caller know that they don't have to proceed further with the
280 channel-open */
281 status = openPacketStreamSSH( stream, SSH_MSG_GLOBAL_REQUEST,
282 &packetOffset );
283 if( cryptStatusError( status ) )
284 return( status );
285 writeString32( stream, "tcpip-forward", 13 );
286 sputc( stream, 0 );
287 writeString32( stream, urlInfo.host, urlInfo.hostLen );
288 writeUint32( stream, urlInfo.port );
289 return( wrapPacketSSH2( sessionInfoPtr, stream, packetOffset ) );
290 }
291 #endif /* 0 */
292
293 /* Send a channel open:
294
295 byte type = SSH_MSG_CHANNEL_OPEN
296 string channel_type
297 uint32 sender_channel
298 uint32 initial_window_size = MAX_WINDOW_SIZE
299 uint32 max_packet_size = bufSize
300 ...
301
302 The use of security protocol-level flow control when there's already
303 a far better, heavily analysed and field-tested network protocol-
304 level flow control mechanism present is just stupid. All it does is
305 create a performance handbrake where throughput can be reduced by as
306 much as an order of magnitude due to SSH's "flow-control" getting in
307 the way (Putty even has an FAQ entry "Why is SFTP so much slower than
308 scp?", for which the correct answer should be "It's the SSH-level
309 flow-control braindamage"). For this reason cryptlib always
310 advertises a maximum window size (effectively disabling the SSH-level
311 flow control) and lets the network stack and network hardware take
312 care of flow control, as they should. Unfortunately some buggy
313 implementations break when sent a window size over a certain limit
314 in which case we have to limit the window size, thus reintroducing
315 the performance handbrake when dealing with these buggy
316 implementations, see the comments for the window handling in
317 ssh2_msg.c for details */
318 status = openPacketStreamSSH( stream, sessionInfoPtr,
319 SSH_MSG_CHANNEL_OPEN );
320 if( cryptStatusError( status ) )
321 return( status );
322 if( serviceType == SERVICE_SUBSYSTEM || serviceType == SERVICE_EXEC )
323 {
324 /* A subsystem is an additional layer on top of the standard
325 channel so we have to open the channel first and then add the
326 subsystem later via a channel request rather than opening it
327 directly. An exec is a special case that works like the default
328 type of session operation, "shell", but doesn't go via a pty */
329 writeString32( stream, "session", 7 );
330 }
331 else
332 {
333 if( serviceType == SERVICE_PORTFORWARD )
334 writeString32( stream, "direct-tcpip", 12 );
335 else
336 {
337 ENSURES( serviceType == SERVICE_SHELL );
338
339 /* It's a generic secure-tunnel that'll be followed by a pty-
340 request and shell */
341 writeString32( stream, "session", 7 );
342 }
343 }
344 writeUint32( stream, channelNo );
345 writeUint32( stream, getWindowSize( sessionInfoPtr ) );
346 status = writeUint32( stream, maxPacketSize );
347 if( serviceType == SERVICE_PORTFORWARD )
348 {
349 /* The caller has requested a port-forwarding channel open, continue
350 the basic channel-open packet with port-forwarding information:
351
352 ...
353 string remote_host_to_connect
354 uint32 rempte_port_to_connect
355 string local_originator_IP_address
356 uint32 local_originator_port */
357 writeString32( stream, urlInfo.host, urlInfo.hostLen );
358 writeUint32( stream, urlInfo.port );
359 writeString32( stream, "127.0.0.1", 9 );
360 status = writeUint32( stream, 22 );
361 }
362 if( cryptStatusOK( status ) )
363 status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
364 if( cryptStatusError( status ) )
365 {
366 sMemDisconnect( stream );
367 return( status );
368 }
369 return( CRYPT_OK );
370 }
371
372 #else
373
374 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
createOpenRequest(INOUT SESSION_INFO * sessionInfoPtr,OUT STREAM * stream,IN_ENUM (SERVICE)const SERVICE_TYPE serviceType,OUT_ENUM_OPT (OPENREQUEST)OPENREQUEST_TYPE * requestType)375 static int createOpenRequest( INOUT SESSION_INFO *sessionInfoPtr,
376 OUT STREAM *stream,
377 IN_ENUM( SERVICE ) const SERVICE_TYPE serviceType,
378 OUT_ENUM_OPT( OPENREQUEST ) \
379 OPENREQUEST_TYPE *requestType )
380 {
381 const long channelNo = getCurrentChannelNo( sessionInfoPtr,
382 CHANNEL_WRITE );
383 const int maxPacketSize = sessionInfoPtr->sendBufSize - \
384 EXTRA_PACKET_SIZE;
385 int status;
386
387 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
388 assert( isWritePtr( stream, sizeof( STREAM ) ) );
389 assert( isWritePtr( requestType, sizeof( OPENREQUEST_TYPE ) ) );
390
391 REQUIRES( serviceType > SERVICE_NONE && serviceType < SERVICE_LAST );
392
393 /* Set the request type to tell the caller what to do after they've sent
394 the initial channel open */
395 *requestType = OPENREQUEST_SESSION;
396
397 /* Send a channel open:
398
399 byte type = SSH_MSG_CHANNEL_OPEN
400 string channel_type
401 uint32 sender_channel
402 uint32 initial_window_size = MAX_WINDOW_SIZE
403 uint32 max_packet_size = bufSize
404 ...
405
406 The use of security protocol-level flow control when there's already
407 a far better, heavily analysed and field-tested network protocol-
408 level flow control mechanism present is just stupid. All it does is
409 create a performance handbrake where throughput can be reduced by as
410 much as an order of magnitude due to SSH's "flow-control" getting in
411 the way (Putty even has an FAQ entry "Why is SFTP so much slower than
412 scp?", for which the correct answer should be "It's the SSH-level
413 flow-control braindamage"). For this reason cryptlib always
414 advertises a maximum window size (effectively disabling the SSH-level
415 flow control) and lets the network stack and network hardware take
416 care of flow control, as they should. Unfortunately some buggy
417 implementations break when sent a window size over a certain limit
418 in which case we have to limit the window size, thus reintroducing
419 the performance handbrake when dealing with these buggy
420 implementations, see the comments for the window handling in
421 ssh2_msg.c for details */
422 status = openPacketStreamSSH( stream, sessionInfoPtr,
423 SSH_MSG_CHANNEL_OPEN );
424 if( cryptStatusError( status ) )
425 return( status );
426 writeString32( stream, "session", 7 );
427 writeUint32( stream, channelNo );
428 writeUint32( stream, getWindowSize( sessionInfoPtr ) );
429 status = writeUint32( stream, maxPacketSize );
430 if( cryptStatusOK( status ) )
431 status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
432 if( cryptStatusError( status ) )
433 {
434 sMemDisconnect( stream );
435 return( status );
436 }
437 return( CRYPT_OK );
438 }
439 #endif /* USE_SSH_EXTENDED */
440
441 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
createSessionOpenRequest(INOUT SESSION_INFO * sessionInfoPtr,INOUT STREAM * stream,IN_ENUM (SERVICE)const SERVICE_TYPE serviceType)442 static int createSessionOpenRequest( INOUT SESSION_INFO *sessionInfoPtr,
443 INOUT STREAM *stream,
444 IN_ENUM( SERVICE ) \
445 const SERVICE_TYPE serviceType )
446 {
447 const long channelNo = getCurrentChannelNo( sessionInfoPtr,
448 CHANNEL_WRITE );
449 int packetOffset, status;
450
451 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
452 assert( isWritePtr( stream, sizeof( STREAM ) ) );
453
454 REQUIRES( serviceType > SERVICE_NONE && serviceType < SERVICE_LAST );
455
456 #ifdef USE_SSH_EXTENDED
457 /* If the caller has requested the use of a custom subsystem (and at the
458 moment the only one that's likely to be used is SFTP), request this
459 from the server by modifying the channel that we've just opened to
460 run the subsystem */
461 if( serviceType == SERVICE_SUBSYSTEM )
462 {
463 BYTE arg1String[ CRYPT_MAX_TEXTSIZE + 8 ];
464 int arg1Len;
465
466 /* Get the subsystem type */
467 status = getChannelAttributeS( sessionInfoPtr,
468 CRYPT_SESSINFO_SSH_CHANNEL_ARG1,
469 arg1String, CRYPT_MAX_TEXTSIZE,
470 &arg1Len );
471 if( cryptStatusError( status ) )
472 return( status );
473
474 /* byte type = SSH_MSG_CHANNEL_REQUEST
475 uint32 recipient_channel
476 string request_name = "subsystem"
477 boolean want_reply = FALSE
478 string subsystem_name */
479 status = openPacketStreamSSH( stream, sessionInfoPtr,
480 SSH_MSG_CHANNEL_REQUEST );
481 if( cryptStatusError( status ) )
482 return( status );
483 writeUint32( stream, channelNo );
484 writeString32( stream, "subsystem", 9 );
485 sputc( stream, 0 );
486 status = writeString32( stream, arg1String, arg1Len );
487 if( cryptStatusOK( status ) )
488 status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
489 return( status );
490 }
491
492 /* If the caller has requested the use of remote command execution (i.e.
493 an rexec rather than the usual SSH rsh), run the command directly
494 without going via a pty */
495 if( serviceType == SERVICE_EXEC )
496 {
497 BYTE arg1String[ CRYPT_MAX_TEXTSIZE + 8 ];
498 int arg1Len;
499
500 /* Get the command to execute */
501 status = getChannelAttributeS( sessionInfoPtr,
502 CRYPT_SESSINFO_SSH_CHANNEL_ARG1,
503 arg1String, CRYPT_MAX_TEXTSIZE,
504 &arg1Len );
505 if( cryptStatusError( status ) )
506 return( status );
507
508 /* byte type = SSH_MSG_CHANNEL_REQUEST
509 uint32 recipient_channel
510 string request_name = "exec"
511 boolean want_reply = FALSE
512 string command */
513 status = openPacketStreamSSH( stream, sessionInfoPtr,
514 SSH_MSG_CHANNEL_REQUEST );
515 if( cryptStatusError( status ) )
516 return( status );
517 writeUint32( stream, channelNo );
518 writeString32( stream, "exec", 4 );
519 sputc( stream, 0 );
520 status = writeString32( stream, arg1String, arg1Len );
521 if( cryptStatusOK( status ) )
522 status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
523 return( status );
524 }
525 #endif /* USE_SSH_EXTENDED */
526
527 REQUIRES( serviceType == SERVICE_SHELL );
528
529 /* It's a standard channel open:
530
531 byte type = SSH_MSG_CHANNEL_REQUEST
532 uint32 recipient_channel
533 string request_name = "pty-req"
534 boolean want_reply = FALSE
535 string TERM_environment_variable = "xterm"
536 uint32 cols = 80
537 uint32 rows = 48
538 uint32 pixel_width = 0
539 uint32 pixel_height = 0
540 string tty_mode_info = ""
541 ... */
542 status = openPacketStreamSSH( stream, sessionInfoPtr,
543 SSH_MSG_CHANNEL_REQUEST );
544 if( cryptStatusError( status ) )
545 return( status );
546 writeUint32( stream, channelNo );
547 writeString32( stream, "pty-req", 7 );
548 sputc( stream, 0 ); /* No reply */
549 writeString32( stream, "xterm", 5 );/* Generic */
550 writeUint32( stream, 80 );
551 writeUint32( stream, 48 ); /* 48 x 80 (24 x 80 is so 1970s) */
552 writeUint32( stream, 0 );
553 writeUint32( stream, 0 ); /* No graphics capabilities */
554 status = writeUint32( stream, 0 ); /* No special TTY modes */
555 if( cryptStatusOK( status ) )
556 status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
557 if( cryptStatusError( status ) )
558 return( status );
559
560 /* ...
561 byte type = SSH_MSG_CHANNEL_REQUEST
562 uint32 recipient_channel
563 string request_name = "shell"
564 boolean want_reply = FALSE
565
566 This final request, once sent, moves the server into interactive
567 session mode */
568 status = continuePacketStreamSSH( stream, SSH_MSG_CHANNEL_REQUEST,
569 &packetOffset );
570 if( cryptStatusError( status ) )
571 return( status );
572 writeUint32( stream, channelNo );
573 writeString32( stream, "shell", 5 );
574 status = sputc( stream, 0 ); /* No reply */
575 if( cryptStatusOK( status ) )
576 status = wrapPacketSSH2( sessionInfoPtr, stream, packetOffset,
577 FALSE, TRUE );
578 return( status );
579 }
580
581 /* Send a channel open */
582
583 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
sendChannelOpen(INOUT SESSION_INFO * sessionInfoPtr)584 int sendChannelOpen( INOUT SESSION_INFO *sessionInfoPtr )
585 {
586 STREAM stream;
587 SERVICE_TYPE serviceType;
588 OPENREQUEST_TYPE requestType;
589 const long channelNo = getCurrentChannelNo( sessionInfoPtr,
590 CHANNEL_READ );
591 long currentChannelNo;
592 int length, value, status;
593
594 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
595
596 /* Make sure that there's channel data available to activate and
597 that it doesn't correspond to an already-active channel */
598 if( channelNo == UNUSED_CHANNEL_NO )
599 {
600 retExt( CRYPT_ERROR_NOTINITED,
601 ( CRYPT_ERROR_NOTINITED, SESSION_ERRINFO,
602 "No current channel information available to activate "
603 "channel" ) );
604 }
605 status = getChannelAttribute( sessionInfoPtr,
606 CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE,
607 &value );
608 if( cryptStatusError( status ) || value )
609 {
610 retExt( CRYPT_ERROR_INITED,
611 ( CRYPT_ERROR_INITED, SESSION_ERRINFO,
612 "Current channel has already been activated" ) );
613 }
614
615 /* Determine the service type that we'll be using */
616 status = getServiceType( sessionInfoPtr, &serviceType );
617 if( cryptStatusError( status ) )
618 return( status );
619
620 /* Create a request for the appropriate type of service */
621 status = createOpenRequest( sessionInfoPtr, &stream, serviceType,
622 &requestType );
623 if( cryptStatusError( status ) )
624 return( status );
625
626 #if 0 /* Never used, see comment in createOpenRequest() for
627 "tcpip-forward" type */
628 /* If it's a request-only message that doesn't open a channel, send it
629 and exit */
630 if( requestType == OPENREQUEST_STANDALONE )
631 {
632 status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
633 sMemDisconnect( &stream );
634 return( status );
635 }
636 #endif /* 0 */
637
638 /* Send the open request to the server. The SSH spec doesn't really
639 explain the semantics of the server's response to the channel open
640 command, in particular whether the returned data size parameters are
641 merely a confirmation of the client's requested values or whether the
642 server is allowed to further modify them to suit its own requirements
643 (or perhaps one is for send and the other for receive?). In the
644 absence of any further guidance we just ignore the returned values,
645 which seems to work for all deployed servers */
646 status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
647 sMemDisconnect( &stream );
648 if( cryptStatusError( status ) )
649 return( status );
650
651 /* Wait for the server's ack of the channel open request:
652
653 byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION
654 uint32 recipient_channel
655 uint32 sender_channel
656 uint32 initial_window_size
657 uint32 maximum_packet_size
658 ... */
659 status = length = \
660 readHSPacketSSH2( sessionInfoPtr, SSH_MSG_SPECIAL_CHANNEL,
661 ID_SIZE + UINT32_SIZE + UINT32_SIZE + \
662 UINT32_SIZE + UINT32_SIZE );
663 if( cryptStatusError( status ) )
664 return( status );
665 sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
666 if( sessionInfoPtr->sessionSSH->packetType == SSH_MSG_CHANNEL_OPEN_FAILURE )
667 {
668 /* The open failed, report the details to the user */
669 status = getOpenFailInfo( sessionInfoPtr, &stream );
670 sMemDisconnect( &stream );
671 return( status );
672 }
673 currentChannelNo = readUint32( &stream );
674 if( currentChannelNo != channelNo )
675 {
676 sMemDisconnect( &stream );
677 retExt( CRYPT_ERROR_BADDATA,
678 ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
679 "Invalid channel number %lX in channel open confirmation, "
680 "should be %lX", currentChannelNo, channelNo ) );
681 }
682 status = currentChannelNo = readUint32( &stream );
683 sMemDisconnect( &stream );
684 if( cryptStatusError( status ) )
685 {
686 retExt( CRYPT_ERROR_BADDATA,
687 ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
688 "Invalid channel data in channel open confirmation for "
689 "channel %lX", channelNo ) );
690 }
691
692 /* The channel has been successfully created, mark it as active and
693 select it for future exchanges */
694 status = setChannelExtAttribute( sessionInfoPtr, SSH_ATTRIBUTE_ACTIVE,
695 TRUE );
696 if( cryptStatusOK( status ) && currentChannelNo != channelNo )
697 {
698 /* It's unclear why anyone would want to use different channel
699 numbers for different directions since it's the same channel that
700 the data is moving across, but Cisco do it anyway */
701 status = setChannelExtAttribute( sessionInfoPtr,
702 SSH_ATTRIBUTE_ALTCHANNELNO,
703 currentChannelNo );
704 }
705 if( cryptStatusOK( status ) )
706 {
707 const int windowSize = getWindowSize( sessionInfoPtr );
708
709 /* The initial window count is the same as the data window size */
710 status = setChannelExtAttribute( sessionInfoPtr,
711 SSH_ATTRIBUTE_WINDOWSIZE,
712 windowSize );
713 if( cryptStatusOK( status ) )
714 status = setChannelExtAttribute( sessionInfoPtr,
715 SSH_ATTRIBUTE_WINDOWCOUNT,
716 windowSize );
717 }
718 if( cryptStatusOK( status ) )
719 status = selectChannel( sessionInfoPtr, channelNo, CHANNEL_BOTH );
720 if( cryptStatusError( status ) )
721 return( status );
722 if( requestType == OPENREQUEST_CHANNELONLY )
723 {
724 /* If we're just opening a new channel in an existing session, we're
725 done */
726 return( CRYPT_OK );
727 }
728 REQUIRES( requestType == OPENREQUEST_SESSION );
729
730 /* It's a session open request that requires additional messages to do
731 anything useful, create and send the extra packets */
732 status = createSessionOpenRequest( sessionInfoPtr, &stream,
733 serviceType );
734 if( cryptStatusOK( status ) )
735 status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
736 sMemDisconnect( &stream );
737 return( status );
738 }
739 #endif /* USE_SSH */
740