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