--- ./cryptlib.h.orig	2024-10-31 00:50:22.528176000 -0400
+++ ./cryptlib.h	2024-10-31 00:50:22.662806000 -0400
@@ -1277,6 +1277,7 @@
 	CRYPT_SESSINFO_SSH_CHANNEL_TERMINAL,	/* TERM string sent to remote */
 	CRYPT_SESSINFO_SSH_CHANNEL_WIDTH,	/* Terminal width */
 	CRYPT_SESSINFO_SSH_CHANNEL_HEIGHT,	/* Terminal height */
+	CRYPT_SESSINFO_SSH_CHANNEL_OPEN,	/* True if write channel is open for read */
 
 	/* Used internally */
 	CRYPT_SESSINFO_LAST, CRYPT_USERINFO_FIRST = 7000,
@@ -1439,7 +1440,7 @@
 	CRYPT_CERTINFO_FIRST_CMS = CRYPT_CERTINFO_CMS_CONTENTTYPE,
 	CRYPT_CERTINFO_LAST_CMS = CRYPT_CERTINFO_LAST - 1,
 	CRYPT_SESSINFO_FIRST_SPECIFIC = CRYPT_SESSINFO_REQUEST,
-	CRYPT_SESSINFO_LAST_SPECIFIC = CRYPT_SESSINFO_SSH_CHANNEL_HEIGHT
+	CRYPT_SESSINFO_LAST_SPECIFIC = CRYPT_SESSINFO_SSH_CHANNEL_OPEN
 
 	/* Point at which private-use values start.  Attribute values sometimes
 	   need to be extended with additional pseudo-values in object-specific
--- ./session/ssh2_channel.c.orig	2024-10-31 00:50:22.433953000 -0400
+++ ./session/ssh2_channel.c	2024-10-31 00:50:22.667097000 -0400
@@ -24,6 +24,7 @@
 #define CHANNEL_FLAG_NONE		0x00	/* No channel flag */
 #define CHANNEL_FLAG_ACTIVE		0x01	/* Channel is active */
 #define CHANNEL_FLAG_WRITECLOSED 0x02	/* Write-side of ch.closed */
+#define CHANNEL_FLAG_READCLOSED 0x04	/* Read-side of ch.closed */
 
 /* Per-channel information.  SSH channel IDs are 32-bit/4 byte data values
    and can be reused during sessions so we provide our own guaranteed-unique
@@ -118,6 +119,8 @@
 		   after */
 		ENSURES( attributeListPtr->valueLength == sizeof( SSH_CHANNEL_INFO ) );
 		channelInfoPtr = attributeListPtr->value;
+		if( channelInfoPtr->flags & CHANNEL_FLAG_READCLOSED)
+			continue;
 		if( isActiveChannel( channelInfoPtr ) && \
 			channelInfoPtr->channelID != excludedChannelID )
 			return( TRUE );
@@ -141,7 +144,9 @@
 	static const CRYPT_ATTRIBUTE_TYPE attributeOrderList[] = {
 			CRYPT_SESSINFO_SSH_CHANNEL, CRYPT_SESSINFO_SSH_CHANNEL_TYPE,
 			CRYPT_SESSINFO_SSH_CHANNEL_ARG1, CRYPT_SESSINFO_SSH_CHANNEL_ARG2,
-			CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE, CRYPT_ATTRIBUTE_NONE,
+			CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE, CRYPT_SESSINFO_SSH_CHANNEL_TERMINAL,
+			CRYPT_SESSINFO_SSH_CHANNEL_WIDTH, CRYPT_SESSINFO_SSH_CHANNEL_HEIGHT,
+			CRYPT_SESSINFO_SSH_CHANNEL_OPEN, CRYPT_ATTRIBUTE_NONE,
 			CRYPT_ATTRIBUTE_NONE };
 	SSH_CHANNEL_INFO *channelInfoPtr = attributeListPtr->value;
 	CRYPT_ATTRIBUTE_TYPE attributeType = channelInfoPtr->cursorPos;
@@ -228,6 +233,7 @@
 			case CRYPT_SESSINFO_SSH_CHANNEL:
 			case CRYPT_SESSINFO_SSH_CHANNEL_TYPE:
 			case CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE:
+			case CRYPT_SESSINFO_SSH_CHANNEL_OPEN:
 				doContinue = FALSE;	/* Always present */
 				break;
 
@@ -476,6 +482,8 @@
 	{
 	const SSH_CHANNEL_INFO *channelInfoPtr = \
 				getCurrentChannelInfo( sessionInfoPtr, CHANNEL_READ );
+	const SSH_CHANNEL_INFO *writeChannelInfoPtr = \
+				getCurrentChannelInfo( sessionInfoPtr, CHANNEL_WRITE );
 
 	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
 	assert( isWritePtr( value, sizeof( int ) ) );
@@ -487,27 +495,38 @@
 	/* Clear return values */
 	*value = 0;
 
-	if( isNullChannel( channelInfoPtr ) )
-		return( CRYPT_ERROR_NOTFOUND );
-
 	switch( attribute )
 		{
 		case CRYPT_SESSINFO_SSH_CHANNEL:
+			if( isNullChannel( channelInfoPtr ) )
+				return( CRYPT_ERROR_NOTFOUND );
 			*value = channelInfoPtr->channelID;
 			return( CRYPT_OK );
 
 		case CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE:
-			*value = isActiveChannel( channelInfoPtr ) ? TRUE : FALSE;
+			if( isNullChannel( writeChannelInfoPtr ) )
+				return( CRYPT_ERROR_NOTFOUND );
+			*value = isActiveChannel( writeChannelInfoPtr ) ? TRUE : FALSE;
 			return( CRYPT_OK );
 
+		case CRYPT_SESSINFO_SSH_CHANNEL_OPEN:
+			if( isNullChannel( writeChannelInfoPtr ) )
+				return( CRYPT_ERROR_NOTFOUND );
+			*value = ( writeChannelInfoPtr->flags & CHANNEL_FLAG_READCLOSED ) ? FALSE : TRUE;
+			return( CRYPT_OK );
+
 		case CRYPT_SESSINFO_SSH_CHANNEL_WIDTH:
-			if (channelInfoPtr->width == 0)
+			if( isNullChannel( writeChannelInfoPtr ) )
+				return( CRYPT_ERROR_NOTFOUND );
+			if (writeChannelInfoPtr->width == 0)
 				return CRYPT_ERROR_NOTFOUND;
 			*value = channelInfoPtr->width;
 			return( CRYPT_OK );
 
 		case CRYPT_SESSINFO_SSH_CHANNEL_HEIGHT:
-			if (channelInfoPtr->height == 0)
+			if( isNullChannel( writeChannelInfoPtr ) )
+				return( CRYPT_ERROR_NOTFOUND );
+			if (writeChannelInfoPtr->height == 0)
 				return CRYPT_ERROR_NOTFOUND;
 			*value = channelInfoPtr->height;
 			return( CRYPT_OK );
@@ -765,7 +784,7 @@
 	channelInfoPtr = findChannelByChannelNo( sessionInfoPtr, channelNo );
 	return( ( channelInfoPtr == NULL ) ? CHANNEL_NONE : \
 			( channelInfoPtr->flags & CHANNEL_FLAG_WRITECLOSED ) ? \
-				CHANNEL_READ : CHANNEL_BOTH );
+				CHANNEL_READ : ( channelInfoPtr->flags & CHANNEL_FLAG_READCLOSED ) ? CHANNEL_WRITE : CHANNEL_BOTH );
 	}
 
 CHECK_RETVAL_ENUM( CHANNEL ) STDC_NONNULL_ARG( ( 1 ) ) \
@@ -785,7 +804,7 @@
 										addrInfoLen );
 	return( ( channelInfoPtr == NULL ) ? CHANNEL_NONE : \
 			( channelInfoPtr->flags & CHANNEL_FLAG_WRITECLOSED ) ? \
-				CHANNEL_READ : CHANNEL_BOTH );
+				CHANNEL_READ : ( channelInfoPtr->flags & CHANNEL_FLAG_READCLOSED ) ? CHANNEL_WRITE : CHANNEL_BOTH );
 	}
 
 /****************************************************************************
@@ -1013,12 +1032,22 @@
 	/* Delete the channel entry.  If we're only closing the write side we
 	   mark the channel as closed for write but leave the overall channel
 	   open */
-	if( channelType == CHANNEL_WRITE )
+	if( channelType == CHANNEL_WRITE  && !(channelInfoPtr->flags & CHANNEL_FLAG_READCLOSED))
 		{
 		REQUIRES( !( channelInfoPtr->flags & CHANNEL_FLAG_WRITECLOSED ) );
 		channelInfoPtr->flags |= CHANNEL_FLAG_WRITECLOSED;
 		if( channelID == sshInfo->currWriteChannel )
 			sshInfo->currWriteChannel = UNUSED_CHANNEL_ID;
+		return( isChannelActive( sessionInfoPtr, \
+								 channelInfoPtr->channelID ) ? \
+				CRYPT_OK : OK_SPECIAL );
+		}
+	if( channelType == CHANNEL_READ && !(channelInfoPtr->flags & CHANNEL_FLAG_WRITECLOSED) && isChannelActive( sessionInfoPtr, channelID ) )
+		{
+		REQUIRES( !( channelInfoPtr->flags & CHANNEL_FLAG_READCLOSED ) );
+		channelInfoPtr->flags |= CHANNEL_FLAG_READCLOSED;
+		if( channelID == sshInfo->currReadChannel )
+			sshInfo->currReadChannel = UNUSED_CHANNEL_ID;
 		return( isChannelActive( sessionInfoPtr, \
 								 channelInfoPtr->channelID ) ? \
 				CRYPT_OK : OK_SPECIAL );
--- ./session/ssh.c.orig	2024-10-31 00:50:22.598316000 -0400
+++ ./session/ssh.c	2024-10-31 00:50:22.670528000 -0400
@@ -556,6 +556,7 @@
 			  type == CRYPT_SESSINFO_SSH_CHANNEL_WIDTH || \
 			  type == CRYPT_SESSINFO_SSH_CHANNEL_HEIGHT || \
 			  type == CRYPT_SESSINFO_SSH_CHANNEL_TERMINAL || \
+			  type == CRYPT_SESSINFO_SSH_CHANNEL_OPEN || \
 			  type == CRYPT_SESSINFO_SSH_PREAUTH );
 #else
 	REQUIRES( type == CRYPT_SESSINFO_SSH_PREAUTH );
@@ -593,7 +594,8 @@
 	if( type == CRYPT_SESSINFO_SSH_CHANNEL || \
 		type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE || \
 		type == CRYPT_SESSINFO_SSH_CHANNEL_WIDTH || \
-		type == CRYPT_SESSINFO_SSH_CHANNEL_HEIGHT)
+		type == CRYPT_SESSINFO_SSH_CHANNEL_HEIGHT || \
+		type == CRYPT_SESSINFO_SSH_CHANNEL_OPEN)
 		{
 		status = getChannelAttribute( sessionInfoPtr, type, data );
 		}
--- ./session/ssh2_msg.c.orig	2023-02-26 03:33:24.000000000 -0500
+++ ./session/ssh2_msg.c	2024-10-31 00:50:22.659472000 -0400
@@ -468,7 +468,7 @@
 											 channelNo ) == CHANNEL_BOTH )
 				{
 				status = sendChannelClose( sessionInfoPtr, channelNo,
-										   CHANNEL_BOTH, TRUE );
+										   CHANNEL_READ, TRUE );
 				}
 			else
 				{
@@ -527,6 +527,8 @@
 	   no more channels left to close */
 	status = deleteChannel( sessionInfoPtr, channelNo, channelType,
 							closeLastChannel  );
+	if( getChannelStatusByChannelNo( sessionInfoPtr, channelNo ) == CHANNEL_NONE )
+		return CRYPT_OK;
 	if( cryptStatusError( status ) )
 		{
 		if( status != OK_SPECIAL )
--- ./kernel/attr_acl.c.orig	2024-10-31 00:50:22.593469000 -0400
+++ ./kernel/attr_acl.c	2024-10-31 00:50:22.675666000 -0400
@@ -3980,6 +3980,12 @@
 		MKPERM_SSH_EXT( RWx_RWx ),
 		ROUTE( OBJECT_TYPE_SESSION ),
 		subACL_SessinfoSSHChannelHeight ),
+	MKACL_N(
+		CRYPT_SESSINFO_SSH_CHANNEL_OPEN,
+		ST_NONE, ST_NONE, ST_SESS_SSH | ST_SESS_SSH_SVR, 
+		MKPERM_TLS( Rxx_Rxx ),
+		ROUTE( OBJECT_TYPE_SESSION ),
+		RANGE( FALSE, TRUE ) ),
 
 	MKACL_END(), MKACL_END()
 	};
@@ -4968,7 +4974,7 @@
 	static_assert( CRYPT_CERTINFO_FIRST_EXTENSION == 2200, "Attribute value" );
 	static_assert( CRYPT_CERTINFO_FIRST_CMS == 2500, "Attribute value" );
 	static_assert( CRYPT_SESSINFO_FIRST_SPECIFIC == 6017, "Attribute value" );
-	static_assert( CRYPT_SESSINFO_LAST_SPECIFIC == 6039, "Attribute value" );
+	static_assert( CRYPT_SESSINFO_LAST_SPECIFIC == 6040, "Attribute value" );
 	static_assert( CRYPT_CERTFORMAT_LAST == 13, "Attribute value" );
 
 	/* Perform a consistency check on the attribute ACLs.  The ACLs are
