internal void SendChannelData(byte[] buf, int offset, int len) { try { if (state != CHANNEL_OPEN) { throw new SSHException("The channel is closed", SSHException.CHANNEL_FAILURE); } if (len > 0) { SSHPacket packet = connection.GetPacket(); packet.WriteByte((System.Byte)SSH_MSG_CHANNEL_DATA); packet.WriteUINT32(remoteid); packet.WriteBinaryString(buf, offset, len); #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_CHANNEL_DATA"); System.Diagnostics.Trace.WriteLine("Channelid=" + ChannelID); #endif connection.SendMessage(packet); } } catch (System.IO.IOException ex) { throw new SSHException(ex.Message, SSHException.INTERNAL_ERROR); } }
/// <summary> /// Disconnect the SSH transport protocol. /// </summary> /// <param name="disconnectReason">A descriptive reason for the disconnection.</param> /// <param name="reason">The SSH reason code.</param> public void Disconnect(String disconnectReason, DisconnectionReason reason) { try { this.disconnectReason = disconnectReason; SSHPacket packet = GetSSHPacket(true); packet.WriteByte(SSH_MSG_DISCONNECT); packet.WriteUINT32((int)reason); packet.WriteString(disconnectReason); packet.WriteString(""); #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_DISCONNECT"); System.Diagnostics.Trace.WriteLine(disconnectReason); #endif SendMessage(packet); } catch { } finally { InternalDisconnect(); } }
/// <summary> /// Close down the output side of the stream. /// </summary> /// <remarks> /// This method sends an SSH_MSG_CHANNEL_EOF message to the remote side. /// </remarks> public override void CloseOutput() { if (!channel.IsClosed && !outputEOF && !channel.closing && !channel.remoteClosed) { SSHPacket packet = channel.connection.GetPacket(); packet.WriteByte(SSH_MSG_CHANNEL_EOF); packet.WriteUINT32(channel.remoteid); try { #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_CHANNEL_EOF"); System.Diagnostics.Trace.WriteLine("Channelid=" + channel.ChannelID); #endif channel.connection.SendMessage(packet); } finally { outputEOF = true; channel.FireEvent(channel, ChannelState.LOCAL_EOF); } } outputEOF = true; }
/// <summary> /// Attempts an authentication using the public/private key pair. /// </summary> /// <param name="authentication"></param> /// <param name="serviceName"></param> public void Authenticate(AuthenticationProtocol authentication, String serviceName) { ByteBuffer baw = new ByteBuffer(); baw.WriteBinaryString(authentication.SessionIdentifier); baw.Write(AuthenticationProtocol.SSH_MSG_USERAUTH_REQUEST); baw.WriteString(Username); baw.WriteString(serviceName); baw.WriteString("publickey"); baw.WriteBool(!VerifyOnly); byte[] encoded; String algorithm; baw.WriteString(algorithm = KeyPair.PublicKey.Algorithm); baw.WriteBinaryString(encoded = KeyPair.PublicKey.GetEncoded()); ByteBuffer baw2 = new ByteBuffer(); // Generate the authentication request baw2.WriteBool(!VerifyOnly); baw2.WriteString(algorithm); baw2.WriteBinaryString(encoded); if (!VerifyOnly) { byte[] signature = KeyPair.PrivateKey.Sign(baw.ToByteArray()); // Format the signature correctly ByteBuffer sig = new ByteBuffer(); sig.WriteString(algorithm); sig.WriteBinaryString(signature); baw2.WriteBinaryString(sig.ToByteArray()); } authentication.SendRequest(Username, serviceName, "publickey", baw2.ToByteArray()); SSHPacket packet = authentication.ReadMessage(); if (packet.MessageID == SSH_MSG_USERAUTH_PK_OK) { throw new SSH2AuthenticationResult(AuthenticationResult.PUBLIC_KEY_ACCEPTABLE, ""); } else { authentication.connection.transport.Disconnect("Unexpected message " + packet.MessageID + " received", DisconnectionReason.PROTOCOL_ERROR); throw new SSHException("Unexpected message " + packet.MessageID + " received", SSHException.PROTOCOL_VIOLATION); } }
/// <summary> /// Process a global message. /// </summary> /// <param name="packet"></param> /// <returns></returns> protected internal override bool ProcessGlobalMessage(SSHPacket packet) { // We need to filter for any messages that require a response from the // connection protocol such as channel open or global requests. These // are not handled anywhere else within this implementation because // doing so would require a thread to wait. try { switch (packet.MessageID) { case SSH_MSG_CHANNEL_OPEN: { #if DEBUG System.Diagnostics.Trace.WriteLine("Received SSH_MSG_CHANNEL_OPEN"); #endif // Attempt to open the channel System.String type = packet.ReadString(); int remoteid = (int)packet.ReadUINT32(); int remotewindow = (int)packet.ReadUINT32(); int remotepacket = (int)packet.ReadUINT32(); byte[] requestdata = packet.Available > 0 ? new byte[packet.Available] : null; if (requestdata != null) { packet.ReadBytes(requestdata); } ProcessChannelOpenRequest(type, remoteid, remotewindow, remotepacket, requestdata); return(true); } case SSH_MSG_GLOBAL_REQUEST: { #if DEBUG System.Diagnostics.Trace.WriteLine("Received SSH_MSG_GLOBAL_REQUEST"); #endif // Attempt to process the global request System.String requestname = packet.ReadString(); bool wantreply = packet.ReadBool(); byte[] requestdata = new byte[packet.Available]; packet.ReadBytes(requestdata); // Process the request ProcessGlobalRequest(requestname, wantreply, requestdata); return(true); } default: return(false); } } catch (System.IO.IOException ex) { throw new SSHException(ex.Message, SSHException.INTERNAL_ERROR); } }
internal void ProcessGlobalRequest(System.String requestname, bool wantreply, byte[] requestdata) { #if DEBUG System.Diagnostics.Trace.WriteLine("Processing global request " + requestname); #endif try { bool success = false; GlobalRequest request = new GlobalRequest(requestname, requestdata); if (requesthandlers.ContainsKey(requestname)) { #if DEBUG System.Diagnostics.Trace.WriteLine("Found handler for request " + requestname); #endif success = ((GlobalRequestHandler)requesthandlers[requestname]).ProcessGlobalRequest(request); } #if DEBUG else { System.Diagnostics.Trace.WriteLine("Cannot find handler for request " + requestname); } #endif if (wantreply) { SSHPacket packet = GetPacket(); if (success) { packet.WriteByte(SSH_MSG_REQUEST_SUCCESS); if (request.Data != null) { packet.WriteBytes(requestdata); } #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_REQUEST_SUCCESS"); #endif transport.SendMessage(packet); } else { // Return a response packet.WriteByte(SSH_MSG_REQUEST_FAILURE); #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_REQUEST_FAILURE"); #endif transport.SendMessage(packet); } } } catch (System.IO.IOException ex) { throw new SSHException(ex.Message, SSHException.INTERNAL_ERROR); } }
public bool WantsNotification(SSHPacket msg) { switch (msg.MessageID) { case SSH_MSG_CHANNEL_CLOSE: return(true); default: return(false); } }
public bool WantsNotification(SSHPacket msg) { switch (msg.MessageID) { case SSH_MSG_REQUEST_SUCCESS: case SSH_MSG_REQUEST_FAILURE: return(true); default: return(false); } }
public bool WantsNotification(SSHPacket msg) { switch (msg.MessageID) { case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: case SSH_MSG_CHANNEL_OPEN_FAILURE: return(true); default: return(false); } }
/// <summary> /// Attempts to authenticate the user using their password. /// </summary> /// <param name="authentication"></param> /// <param name="servicename"></param> public void Authenticate(AuthenticationProtocol authentication, String servicename) { try { if (Username == null || Password == null) { throw new SSHException("Username or password not set!", SSHException.BAD_API_USAGE); } if (passwordChangeRequired && newpassword == null) { throw new SSHException("You must set a new password!", SSHException.BAD_API_USAGE); } ByteBuffer buf = new ByteBuffer(); buf.WriteBool(passwordChangeRequired); buf.WriteString(Password); if (passwordChangeRequired) { buf.WriteString(newpassword); } authentication.SendRequest(Username, servicename, "password", buf.ToByteArray()); // We need to read the response since we may have password change. SSHPacket packet = authentication.ReadMessage(); if (packet.MessageID != SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) { authentication.transport.Disconnect( "Unexpected message received", DisconnectionReason.PROTOCOL_ERROR); throw new SSHException( "Unexpected response from Authentication Protocol", SSHException.PROTOCOL_VIOLATION); } passwordChangeRequired = true; throw new SSH2AuthenticationResult(AuthenticationResult.FAILED, ""); } catch (IOException ex) { throw new SSHException(ex.Message, SSHException.INTERNAL_ERROR); } }
/// <summary> /// Sends a global request. /// </summary> /// <param name="request"></param> /// <param name="wantreply"></param> /// <returns></returns> public bool SendGlobalRequest(GlobalRequest request, bool wantreply) { try { SSHPacket packet = GetPacket(); packet.WriteByte(SSH_MSG_GLOBAL_REQUEST); packet.WriteString(request.Name); packet.WriteBool(wantreply); if (request.Data != null) { packet.WriteBytes(request.Data); } #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_GLOBAL_REQUEST"); System.Diagnostics.Trace.WriteLine(request.Name); #endif SendMessage(packet); if (wantreply) { packet = GlobalMessages.NextMessage(GLOBAL_REQUEST_MESSAGES); if (packet.MessageID == SSH_MSG_REQUEST_SUCCESS) { if (packet.Available > 1) { byte[] tmp = new byte[packet.Available]; packet.ReadBytes(tmp); request.Data = tmp; } else { request.Data = null; } return(true); } else { return(false); } } else { return(true); } } catch (System.IO.IOException ex) { throw new SSHException(ex.Message, SSHException.INTERNAL_ERROR); } }
/// <summary> /// Process a channel request /// </summary> /// <param name="requesttype">the name of the request, for example "pty-req"</param> /// <param name="wantreply">specifies whether the remote side should send a success/failure message</param> /// <param name="requestdata">the request data</param> protected internal virtual void ChannelRequest(String requesttype, bool wantreply, byte[] requestdata) { if (wantreply) { SSHPacket packet = connection.GetPacket(); packet.WriteByte(SSH_MSG_CHANNEL_FAILURE); #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_CHANNEL_FAILURE"); System.Diagnostics.Trace.WriteLine("Channelid=" + ChannelID); #endif connection.SendMessage(packet); } }
/// <summary> /// Sends a close message to the remote side without waiting for a reply /// </summary> public override void SendAsyncClose() { bool performClose = false; lock (this) { if (!closing) { performClose = closing = true; } } if (state == CHANNEL_OPEN && performClose) { try { // Close the ChannelOutputStream stream.Close(); // Send our close message SSHPacket packet = connection.GetPacket(); packet.WriteByte((System.Byte)SSH_MSG_CHANNEL_CLOSE); packet.WriteUINT32(remoteid); try { #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_CHANNEL_CLOSE"); System.Diagnostics.Trace.WriteLine("Channelid=" + ChannelID); #endif connection.SendMessage(packet); } catch (SSHException) { } } catch (System.IO.EndOfStreamException) { // Ignore this is the message store informing of close/eof } catch (System.IO.IOException ex) { // IO Error during close so the connection has dropped connection.SignalClosingState(); connection.transport.Disconnect("IOException during channel close: " + ex.Message, DisconnectionReason.CONNECTION_LOST); } } }
/// <summary> /// /// </summary> /// <param name="clientId"></param> /// <param name="serverId"></param> /// <param name="clientKexInit"></param> /// <param name="serverKexInit"></param> public override void PerformClientExchange(String clientId, String serverId, byte[] clientKexInit, byte[] serverKexInit) { this.clientId = clientId; this.serverId = serverId; this.clientKexInit = clientKexInit; this.serverKexInit = serverKexInit; BigInteger q = p.subtract(BigInteger.ONE).divide(g); do { x = new BigInteger(p.bitLength(), new RNGCryptoServiceProvider()); }while((x.compareTo(BigInteger.ONE) < 0) && (x.compareTo(q) > 0)); e = g.modPow(x, p); if (e.compareTo(BigInteger.ONE) < 0 || e.compareTo(p.subtract(BigInteger.ONE)) > 0) { throw new SSHException("Key exchange failed to generate e value", SSHException.INTERNAL_ERROR); } SSHPacket packet = transport.GetSSHPacket(true); packet.WriteByte(SSH_MSG_KEXDH_INIT); packet.WriteBigInteger(e); transport.SendMessage(packet); packet = transport.NextMessage(); if (packet.MessageID != SSH_MSG_KEXDH_REPLY) { throw new SSHException("Expected SSH_MSG_KEXDH_REPLY but got message id " + packet.MessageID, SSHException.KEY_EXCHANGE_FAILED); } hostKey = packet.ReadBinaryString(); f = packet.ReadBigInteger(); signature = packet.ReadBinaryString(); secret = f.modPow(x, p); CalculateExchangeHash(); }
internal void StartService(String serviceName) { SSHPacket packet = GetSSHPacket(true); packet.WriteByte(SSH_MSG_SERVICE_REQUEST); packet.WriteString(serviceName); #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_SERVICE_REQUEST"); System.Diagnostics.Trace.WriteLine(serviceName); #endif SendMessage(packet); do { packet = ReadMessage(); }while(ProcessMessage(packet) || packet.MessageID != SSH_MSG_SERVICE_ACCEPT); }
internal void SendKeyExchangeInit() { try { FireStateChange(TransportProtocolState.PERFORMING_KEYEXCHANGE); numIncomingBytesSinceKEX = 0; numIncomingSSHPacketsSinceKEX = 0; numOutgoingBytesSinceKEX = 0; numOutgoingSSHPacketsSinceKEX = 0; currentState = TransportProtocolState.PERFORMING_KEYEXCHANGE; SSHPacket packet = GetSSHPacket(true); SSH2Context transportContext = (SSH2Context)client.Context; byte[] cookie = new byte[16]; rnd.GetBytes(cookie); packet.WriteByte((byte)SSH_MSG_KEX_INIT); packet.WriteBytes(cookie); packet.WriteString("diffie-hellman-group1-sha1"); packet.WriteString(transportContext.SupportedPublicKeys.List(transportContext.PreferredPublicKey)); packet.WriteString(transportContext.SupportedCiphers.List(transportContext.PreferredCipherCS)); packet.WriteString(transportContext.SupportedCiphers.List(transportContext.PreferredCipherSC)); packet.WriteString(transportContext.SupportedMACs.List(transportContext.PreferredMacCS)); packet.WriteString(transportContext.SupportedMACs.List(transportContext.PreferredMacSC)); packet.WriteString("none"); packet.WriteString("none"); packet.WriteString(""); packet.WriteString(""); packet.WriteByte((byte)0); packet.WriteUINT32(0); #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_KEX_INIT"); #endif localkex = SendMessage(packet, true); } catch (System.IO.IOException ex) { throw new SSHException(ex.Message, SSHException.INTERNAL_ERROR); } }
internal bool ProcessMessage(SSHPacket packet) { try { switch (packet.MessageID) { case SSH_MSG_USERAUTH_FAILURE: { String auths = packet.ReadString(); throw new SSH2AuthenticationResult( packet.ReadBool() ? AuthenticationResult.FURTHER_AUTHENTICATION_REQUIRED : AuthenticationResult.FAILED, auths); } case SSH_MSG_USERAUTH_SUCCESS: { connection.Start(); throw new SSH2AuthenticationResult(AuthenticationResult.COMPLETE, ""); } case SSH_MSG_USERAUTH_BANNER: { if (((SSH2Context)transport.Context).Banner != null) { ((SSH2Context)transport.Context).Banner.DisplayBanner(packet.ReadString()); } return(true); } default: return(false); } } catch (IOException ex) { throw new SSHException(ex.Message, SSHException.INTERNAL_ERROR); } }
internal void AdjustWindow(int increment) { try { SSHPacket packet = connection.GetPacket(); packet.WriteByte((System.Byte)SSH_MSG_WINDOW_ADJUST); packet.WriteUINT32(remoteid); packet.WriteUINT32(increment); localwindow.adjust(increment); #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_WINDOW_ADJUST"); System.Diagnostics.Trace.WriteLine("Channelid=" + ChannelID); #endif connection.SendMessage(packet); } catch (System.IO.IOException ex) { throw new SSHException(ex.Message, SSHException.INTERNAL_ERROR); } }
/// <summary> /// Sends a channel request. Many channels have extensions that are specific to that particular /// channel type, an example of which is requesting a pseudo terminal from an interactive session. /// </summary> /// <param name="requesttype">the name of the request, for example "pty-req"</param> /// <param name="wantreply">specifies whether the remote side should send a success/failure message</param> /// <param name="requestdata">the request data</param> /// <returns></returns> public bool SendRequest(System.String requesttype, bool wantreply, byte[] requestdata) { lock (this) { try { SSHPacket packet = connection.GetPacket(); packet.WriteByte((System.Byte)SSH_MSG_CHANNEL_REQUEST); packet.WriteUINT32(remoteid); packet.WriteString(requesttype); packet.WriteBool(wantreply); if (requestdata != null) { packet.WriteBytes(requestdata); } #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_CHANNEL_REQUEST " + requesttype); System.Diagnostics.Trace.WriteLine("Channelid=" + ChannelID); #endif connection.SendMessage(packet); bool result = false; if (wantreply) { packet = ProcessMessages(CHANNEL_REQUEST_MESSAGES); return(packet.MessageID == SSH_MSG_CHANNEL_SUCCESS); } return(result); } catch (System.IO.IOException ex) { throw new SSHException(ex.Message, SSHException.INTERNAL_ERROR); } } }
/// <summary> /// Write data to the SSH stream. /// </summary> /// <param name="buf">The destination byte array.</param> /// <param name="offset">The offset to start taking data for writing.</param> /// <param name="len">The number of bytes to write.</param> public override void Write(byte[] buf, int offset, int len) { int write; do { if (outputEOF) { throw new SSHException("The channel is EOF", SSHException.CHANNEL_FAILURE); } if (channel.IsClosed) { throw new SSHException("The channel is closed!", SSHException.CHANNEL_FAILURE); } if (channel.remotewindow.available() <= 0) { SSHPacket packet = channel.ProcessMessages(WINDOW_ADJUST_MESSAGES); } write = channel.remotewindow.available() < channel.remotewindow.PacketSize ? (channel.remotewindow.available() < len ? channel.remotewindow.available():len) : (channel.remotewindow.PacketSize < len?channel.remotewindow.PacketSize:len); if (write > 0) { channel.SendChannelData(buf, offset, write); channel.remotewindow.consume(write); len -= write; offset += write; } }while (len > 0); channel.FireOutputListenerEvent(buf, offset, len); }
/// <summary> /// Send an authentication request. This method will be called from the <see cref="Maverick.SSH2.SSH2AuthenticationClient.Authenticate"/> /// method to initiate the authentication proceedure. /// </summary> /// <param name="username"></param> /// <param name="servicename"></param> /// <param name="methodname"></param> /// <param name="requestdata"></param> public void SendRequest(String username, String servicename, String methodname, byte[] requestdata) { try { SSHPacket packet = transport.GetSSHPacket(true); packet.WriteByte(SSH_MSG_USERAUTH_REQUEST); packet.WriteString(username); packet.WriteString(servicename); packet.WriteString(methodname); if (requestdata != null) { packet.WriteBytes(requestdata); } transport.SendMessage(packet); } catch (IOException ex) { throw new SSHException(ex.Message, SSHException.INTERNAL_ERROR); } }
/// <summary> /// Processes a channel message. /// </summary> /// <param name="msg"></param> /// <returns></returns> protected internal override bool ProcessChannelMessage(SSHChannelMessage msg) { try { #if DEBUG LogMessage(msg); #endif switch (msg.MessageID) { case SSH_MSG_CHANNEL_REQUEST: { String requesttype = msg.ReadString(); bool wantreply = msg.ReadBool(); byte[] requestdata = new byte[msg.Available]; msg.ReadBytes(requestdata); ChannelRequest(requesttype, wantreply, requestdata); return(true); } case SSH_MSG_CHANNEL_DATA: { int count = (int)SSHPacket.ReadInt(msg.Payload, 5); if (autoConsumeInput) { localwindow.consume(count); if (localwindow.available() <= stream.buffer.Length / 2 && !IsClosed) { AdjustWindow(stream.buffer.Length - localwindow.available()); } } // Fire the input listener event FireInputListenerEvent(msg.Payload, 9, count); return(autoConsumeInput); } case SSH_MSG_CHANNEL_EXTENDED_DATA: { int type = (int)SSHPacket.ReadInt(msg.Payload, 5); int count = (int)SSHPacket.ReadInt(msg.Payload, 9); if (autoConsumeInput) { localwindow.consume(count); if (localwindow.available() <= stream.buffer.Length / 2 && !IsClosed) { AdjustWindow(stream.buffer.Length - localwindow.available()); } } FireErrorStreamListenerEvent(type, msg.Payload, 13, count); return(autoConsumeInput); } case SSH_MSG_CHANNEL_CLOSE: { remoteClosed = true; CheckCloseStatus(true); return(false); } case SSH_MSG_CHANNEL_EOF: { FireEvent(this, ChannelState.REMOTE_EOF); ChannelEOF(); return(false); } default: return(false); } } catch (System.IO.IOException ex) { throw new SSHException(ex.Message, SSHException.INTERNAL_ERROR); } }
/// <summary> /// Send a message to the remote side of the connection. /// </summary> /// <param name="packet"></param> /// <param name="returnPayload">Return the unecrypted payload of this packet.</param> public byte[] SendMessage(SSHPacket packet, bool returnPayload) { byte[] payload = null; lock (this) { if (currentState == TransportProtocolState.PERFORMING_KEYEXCHANGE && !IsTransportMessage(packet.MessageID)) { lock (kexqueue) { kexqueue.Add(packet); return(payload); } } try { int padding = 4; // Compress the payload if necersary /*if (outgoingCompression != null) * { * msgdata = outgoingCompression.compress(msgdata, 0, msgdata.Length); * }*/ // Determine the padding length padding += ((outgoingCipherLength - ((packet.Length + padding) % outgoingCipherLength)) % outgoingCipherLength); packet.MoveToPosition(0); // Write the packet length field packet.WriteUINT32(packet.Length - 4 + padding); // Write the padding length packet.WriteByte((byte)padding); // Now skip back up to the end of the packet packet.MoveToEnd(); if (returnPayload) { payload = packet.Payload; } // Create some random data for the padding byte[] pad = new byte[padding]; rnd.GetBytes(pad); packet.WriteBytes(pad); // Generate the MAC if (outgoingMac != null) { outgoingMac.Generate(outgoingSequence, packet.Array, 0, packet.Length, packet.Array, packet.Length); } // Perfrom encrpytion if (encryption != null) { encryption.Transform(packet.Array, 0, packet.Array, 0, packet.Length); } packet.Skip(outgoingMacLength); outgoingBytes += packet.Length; // Send! packet.WriteToStream(transport.GetStream()); outgoingSequence++; numOutgoingBytesSinceKEX += (uint)packet.Length; numOutgoingSSHPacketsSinceKEX++; ReleaseSSHPacket(packet); if (outgoingSequence > 4294967295) { outgoingSequence = 0; } if (numOutgoingBytesSinceKEX >= MAX_NUM_BYTES_BEFORE_REKEY || numOutgoingSSHPacketsSinceKEX >= MAX_NUM_PACKETS_BEFORE_REKEY) { SendKeyExchangeInit(); } } catch (System.IO.IOException ex) { InternalDisconnect(); throw new SSHException("Unexpected termination: " + ex.Message, SSHException.UNEXPECTED_TERMINATION); } catch (System.ObjectDisposedException ex) { InternalDisconnect(); throw new SSHException("Unexpected terminaton: " + ex.Message, SSHException.UNEXPECTED_TERMINATION); } } return(payload); }
/// <summary> /// Send a message to the remote side of the connection. /// </summary> /// <param name="packet"></param> public byte[] SendMessage(SSHPacket packet) { return(SendMessage(packet, false)); }
internal SSHPacket ReadMessage() { lock (kexlock) { try { SSHPacket packet = GetSSHPacket(false); packet.ReadFromStream(transport.GetStream(), incomingCipherLength); // Mark the current position so we can read more data packet.Mark(); packet.MoveToPosition(0); // Decrypt the data if we have a valid cipher if (decryption != null) { decryption.Transform(packet.Array, 0, packet.Array, 0, incomingCipherLength); } int msglen = (int)packet.ReadUINT32(); int padlen = packet.ReadByte(); int remaining = (msglen - (incomingCipherLength - 4)); // Verify that the packet length is good if (remaining < 0) { InternalDisconnect(); throw new SSHException("EOF whilst reading message data block", SSHException.UNEXPECTED_TERMINATION); } else if (remaining > packet.Limit - packet.Length) { InternalDisconnect(); throw new SSHException("Incoming packet length violates SSH protocol", SSHException.UNEXPECTED_TERMINATION); } // Read, decrypt and save the remaining data packet.MoveToMark(); packet.ReadFromStream(transport.GetStream(), remaining); if (decryption != null) { decryption.Transform(packet.Array, incomingCipherLength, packet.Array, incomingCipherLength, remaining); } // Tell the packet where the payload ends //packet.PayloadLength = (int)msglen - padlen - 1; if (incomingMac != null) { packet.ReadFromStream(transport.GetStream(), incomingMacLength); // Verify the mac if (!incomingMac.Verify(incomingSequence, packet.Array, 0, incomingCipherLength + remaining, packet.Array, incomingCipherLength + remaining)) { Disconnect("Corrupt Mac on input", DisconnectionReason.MAC_ERROR); throw new SSHException("Corrupt Mac on input", SSHException.PROTOCOL_VIOLATION); } } if (++incomingSequence > 4294967295) { incomingSequence = 0; } incomingBytes += incomingCipherLength + remaining + incomingMacLength; // Uncompress the message payload if necersary /*if (incomingCompression != null) * { * return incomingCompression.uncompress(payload, 0, payload.Length); * }*/ numIncomingBytesSinceKEX += (uint)packet.Length; numIncomingSSHPacketsSinceKEX++; if (numIncomingBytesSinceKEX >= MAX_NUM_BYTES_BEFORE_REKEY || numIncomingSSHPacketsSinceKEX >= MAX_NUM_PACKETS_BEFORE_REKEY) { SendKeyExchangeInit(); } // Get the packet ready for reading packet.MoveToPosition(6); return(packet); } catch (System.ObjectDisposedException ex) { InternalDisconnect(); throw new SSHException("Unexpected terminaton: " + ex.Message, SSHException.UNEXPECTED_TERMINATION); } catch (System.IO.IOException ex) { InternalDisconnect(); throw new SSHException("Unexpected terminaton: " + (ex.Message != null? ex.Message:ex.GetType().FullName) + " sequenceNo = " + incomingSequence + " bytesIn = " + incomingBytes + " bytesOut = " + outgoingBytes, SSHException.UNEXPECTED_TERMINATION); } } }
internal void ReleaseSSHPacket(SSHPacket packet) { // TODO: Add the packet back into the pool }
internal void PerformKeyExchange(SSHPacket packet) { lock (kexlock) { if (localkex == null) { SendKeyExchangeInit(); } SSH2Context context = (SSH2Context)client.Context; currentState = TransportProtocolState.PERFORMING_KEYEXCHANGE; remotekex = packet.Payload; // Ignore the cookie packet.Skip(16); String remoteKeyExchanges = packet.ReadString(); String remotePublicKeys = packet.ReadString(); String remoteCiphersCS = packet.ReadString(); String remoteCiphersSC = packet.ReadString(); String remoteHMacCS = packet.ReadString(); String remoteHMacSC = packet.ReadString(); String cipherCS = SelectNegotiatedComponent(context.SupportedCiphers.List( context.PreferredCipherCS), remoteCiphersCS); String cipherSC = SelectNegotiatedComponent(context.SupportedCiphers.List( context.PreferredCipherSC), remoteCiphersSC); Cipher encryption = (Cipher)context.SupportedCiphers.GetInstance(cipherCS); Cipher decryption = (Cipher)context.SupportedCiphers.GetInstance(cipherSC); String macCS = SelectNegotiatedComponent(context.SupportedMACs.List( context.PreferredMacCS), remoteHMacCS); String macSC = SelectNegotiatedComponent(context.SupportedMACs.List( context.PreferredMacSC), remoteHMacSC); SSH2Hmac outgoingMac = (SSH2Hmac)context.SupportedMACs.GetInstance(macCS); SSH2Hmac incomingMac = (SSH2Hmac)context.SupportedMACs.GetInstance(macSC); // Ignore compression and languages as were not interested in that atm // Create a Key Exchange instance String kex = SelectNegotiatedComponent(context.SupportedKeyExchanges.List( context.PreferredKeyExchange), remoteKeyExchanges); String publickey = SelectNegotiatedComponent(context.SupportedPublicKeys.List( context.PreferredPublicKey), remotePublicKeys); #if DEBUG // Output the local settings System.Diagnostics.Trace.WriteLine("Local Key exchange settings follow:"); System.Diagnostics.Trace.WriteLine("Key exchange: " + context.SupportedKeyExchanges.List( context.PreferredKeyExchange)); System.Diagnostics.Trace.WriteLine("Public keys : " + context.SupportedPublicKeys.List( context.PreferredPublicKey)); System.Diagnostics.Trace.WriteLine("Ciphers client->server: " + context.SupportedCiphers.List( context.PreferredCipherCS)); System.Diagnostics.Trace.WriteLine("Ciphers server->client: " + context.SupportedCiphers.List( context.PreferredCipherSC)); System.Diagnostics.Trace.WriteLine("HMAC client->server: " + context.SupportedMACs.List( context.PreferredMacCS)); System.Diagnostics.Trace.WriteLine("HMAC server->client: " + context.SupportedMACs.List( context.PreferredMacSC)); // Output the remote settings System.Diagnostics.Trace.WriteLine("Remote Key exchange settings follow:"); System.Diagnostics.Trace.WriteLine("Key exchange: " + remoteKeyExchanges); System.Diagnostics.Trace.WriteLine("Public keys : " + remotePublicKeys); System.Diagnostics.Trace.WriteLine("Ciphers client->server: " + remoteCiphersCS); System.Diagnostics.Trace.WriteLine("Ciphers server->client: " + remoteCiphersSC); System.Diagnostics.Trace.WriteLine("HMAC client->server: " + remoteHMacCS); System.Diagnostics.Trace.WriteLine("HMAC server->client: " + remoteHMacSC); // Output the selected settigns System.Diagnostics.Trace.WriteLine("Selected kex exchange: " + kex); System.Diagnostics.Trace.WriteLine("Selected public key: " + publickey); System.Diagnostics.Trace.WriteLine("Selected cipher client->server: " + cipherCS); System.Diagnostics.Trace.WriteLine("Selected cipher server->client: " + cipherSC); System.Diagnostics.Trace.WriteLine("Selected HMAC client->server: " + macCS); System.Diagnostics.Trace.WriteLine("Selected HMAC server->client: " + macSC); #endif keyexchange = (SSH2KeyExchange)context.SupportedKeyExchanges.GetInstance(kex); keyexchange.Init(this); // Perform the key exchange keyexchange.PerformClientExchange(client.LocalIdentification, client.RemoteIdentification, localkex, remotekex); SSHPublicKey hostkey = (SSHPublicKey)context.SupportedPublicKeys.GetInstance(publickey); hostkey.Init(keyexchange.HostKey, 0, keyexchange.HostKey.Length); if (context.KnownHosts != null) { if (!context.KnownHosts.VerifyHost(transport.Hostname, hostkey)) { Disconnect("Host key not accepted", DisconnectionReason.HOST_KEY_NOT_VERIFIABLE); throw new SSHException("The host key was not accepted", SSHException.CANCELLED_CONNECTION); } } if (!hostkey.VerifySignature(keyexchange.Signature, keyexchange.ExchangeHash)) { Disconnect("The host key signature is invalid", DisconnectionReason.HOST_KEY_NOT_VERIFIABLE); throw new SSHException("The host key signature is invalid", SSHException.PROTOCOL_VIOLATION); } if (sessionIdentifier == null) { sessionIdentifier = keyexchange.ExchangeHash; } packet = GetSSHPacket(true); packet.WriteByte(SSH_MSG_NEWKEYS); #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_NEWKEYS"); #endif SendMessage(packet); encryption.Init(Cipher.ENCRYPT_MODE, MakeSSHKey('A'), MakeSSHKey('C')); outgoingCipherLength = encryption.BlockSize; outgoingMac.Init(MakeSSHKey('E')); outgoingMacLength = outgoingMac.MacLength; this.encryption = encryption; this.outgoingMac = outgoingMac; do { packet = ReadMessage(); // Process the transport protocol message, must only be // SSH_MSH_INGORE, SSH_MSG_DEBUG, SSH_MSG_DISCONNECT or SSH_MSG_NEWKEYS if (!ProcessMessage(packet)) { Disconnect("Invalid message received during key exchange", DisconnectionReason.PROTOCOL_ERROR); throw new SSHException( "Invalid message received during key exchange", SSHException.PROTOCOL_VIOLATION); } }while(packet.MessageID != SSH_MSG_NEWKEYS); // Put the incoming components into use decryption.Init(Cipher.DECRYPT_MODE, MakeSSHKey('B'), MakeSSHKey('D')); incomingCipherLength = decryption.BlockSize; incomingMac.Init(MakeSSHKey('F')); incomingMacLength = incomingMac.MacLength; this.decryption = decryption; this.incomingMac = incomingMac; //this.incomingCompression = incomingCompression; currentState = TransportProtocolState.CONNECTED; FireStateChange(TransportProtocolState.CONNECTED); lock (kexqueue) { #if DEBUG System.Diagnostics.Trace.WriteLine("Sending queued messages"); #endif for (System.Collections.IEnumerator e = kexqueue.GetEnumerator(); e.MoveNext();) { SendMessage((SSHPacket)e.Current); } kexqueue.Clear(); } // Clean up and reset any parameters localkex = null; remotekex = null; } }
internal bool ProcessMessage(SSHPacket packet) { try { if (packet.Length < 1) { Disconnect("Invalid message received", DisconnectionReason.PROTOCOL_ERROR); throw new SSHException("Invalid transport protocol message", SSHException.INTERNAL_ERROR); } switch (packet.MessageID) { case SSH_MSG_DISCONNECT: { packet.ReadInt(); #if DEBUG System.Diagnostics.Trace.WriteLine("Received SSH_MSG_DISCONNECT: " + packet.ReadString()); #endif InternalDisconnect(); throw new SSHException(packet.ReadString(), SSHException.REMOTE_HOST_DISCONNECTED); } case SSH_MSG_IGNORE: { #if DEBUG System.Diagnostics.Trace.WriteLine("Received SSH_MSG_IGNORE"); #endif return(true); } case SSH_MSG_DEBUG: { #if DEBUG System.Diagnostics.Trace.WriteLine("Received SSH_MSG_DEBUG"); packet.Skip(1); System.Diagnostics.Trace.WriteLine(packet.ReadString()); #endif return(true); } case SSH_MSG_NEWKEYS: { // This lightweight implemention ignores these messages #if DEBUG System.Diagnostics.Trace.WriteLine("Received SSH_MSG_NEWKEYS"); #endif return(true); } case SSH_MSG_KEX_INIT: { #if DEBUG System.Diagnostics.Trace.WriteLine("Received SSH_MSG_KEX_INIT"); #endif if (remotekex != null) { Disconnect("Key exchange already in progress!", DisconnectionReason.PROTOCOL_ERROR); throw new SSHException("Key exchange already in progress!", SSHException.PROTOCOL_VIOLATION); } PerformKeyExchange(packet); return(true); } default: { // Not a transport protocol message return(false); } } } catch (System.IO.IOException ex1) { throw new SSHException(ex1.Message, SSHException.INTERNAL_ERROR); } }
internal void LogMessage(SSHPacket packet) { switch (packet.MessageID) { case SSH_MSG_CHANNEL_OPEN_FAILURE: { System.Diagnostics.Trace.WriteLine("Received SSH_MSG_CHANNEL_OPEN_FAILURE for channel id " + ChannelID); return; } case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: { System.Diagnostics.Trace.WriteLine("Received SSH_MSG_CHANNEL_OPEN_CONFIRMATION for channel id " + ChannelID); return; } case SSH_MSG_CHANNEL_CLOSE: { System.Diagnostics.Trace.WriteLine("Received SSH_MSG_CHANNEL_CLOSE for channel id " + ChannelID); return; } case SSH_MSG_CHANNEL_EOF: { System.Diagnostics.Trace.WriteLine("Received SSH_MSG_CHANNEL_EOF for channel id " + ChannelID); return; } case SSH_MSG_CHANNEL_REQUEST: { System.Diagnostics.Trace.WriteLine("Received SSH_MSG_CHANNEL_REQUEST for channel id " + ChannelID); return; } case SSH_MSG_CHANNEL_SUCCESS: { System.Diagnostics.Trace.WriteLine("Received SSH_MSG_CHANNEL_SUCCESS for channel id " + ChannelID); return; } case SSH_MSG_CHANNEL_FAILURE: { System.Diagnostics.Trace.WriteLine("Received SSH_MSG_CHANNEL_FAILURE for channel id " + ChannelID); return; } case SSH_MSG_WINDOW_ADJUST: { System.Diagnostics.Trace.WriteLine("Received SSH_MSG_WINDOW_ADJUST for channel id " + ChannelID); return; } case SSH_MSG_CHANNEL_DATA: { System.Diagnostics.Trace.WriteLine("Received SSH_MSG_CHANNEL_DATA for channel id " + ChannelID); return; } case SSH_MSG_CHANNEL_EXTENDED_DATA: { System.Diagnostics.Trace.WriteLine("Received SSH_MSG_CHANNEL_EXTENDED_DATA for channel id " + ChannelID); return; } } }
/// <summary> /// Process a channel open request. /// </summary> /// <param name="type"></param> /// <param name="remoteid"></param> /// <param name="remotewindow"></param> /// <param name="remotepacket"></param> /// <param name="requestdata"></param> internal void ProcessChannelOpenRequest(System.String type, int remoteid, int remotewindow, int remotepacket, byte[] requestdata) { try { SSHPacket packet = GetPacket(); if (channelfactories.ContainsKey(type)) { try { SSH2Channel channel = ((ChannelFactory)channelfactories[type]).CreateChannel(type, requestdata); // Allocate a channel int localid = AllocateChannel(channel); if (localid > -1) { try { channel.Init(this, localid); byte[] responsedata = channel.Create(); packet.WriteByte(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); packet.WriteUINT32(remoteid); packet.WriteUINT32(localid); packet.WriteUINT32(channel.WindowSize); packet.WriteUINT32(channel.PacketSize); if (responsedata != null) { packet.WriteBytes(requestdata); } #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_CHANNEL_OPEN_CONFIRMATION"); System.Diagnostics.Trace.WriteLine("Channelid=" + localid); System.Diagnostics.Trace.WriteLine("Remoteid=" + remoteid); #endif transport.SendMessage(packet); channel.Open(remoteid, remotewindow, remotepacket); return; } catch (SSHException ex) { #if DEBUG System.Diagnostics.Trace.WriteLine("Exception occured whilst opening channel"); System.Diagnostics.Trace.WriteLine(ex.StackTrace); #endif packet.WriteByte(SSH_MSG_CHANNEL_OPEN_FAILURE); packet.WriteUINT32(remoteid); packet.WriteUINT32(ChannelOpenException.CONNECT_FAILED); packet.WriteString(ex.Message); packet.WriteString(""); } } else { #if DEBUG System.Diagnostics.Trace.WriteLine("Maximum allowable open channel limit of " + MaximumNumChannels + " exceeded!"); #endif packet.WriteByte(SSH_MSG_CHANNEL_OPEN_FAILURE); packet.WriteUINT32(remoteid); packet.WriteUINT32(ChannelOpenException.RESOURCE_SHORTAGE); packet.WriteString("Maximum allowable open channel limit of " + MaximumNumChannels + " exceeded!"); packet.WriteString(""); } } catch (ChannelOpenException ex) { #if DEBUG System.Diagnostics.Trace.WriteLine("Channel open exception occured whilst opening channel"); System.Diagnostics.Trace.WriteLine(ex.StackTrace); #endif packet.WriteByte(SSH_MSG_CHANNEL_OPEN_FAILURE); packet.WriteUINT32(remoteid); packet.WriteUINT32(ex.Reason); packet.WriteString(ex.Message); packet.WriteString(""); } } else { #if DEBUG System.Diagnostics.Trace.WriteLine(type + " is not a supported channel type"); #endif packet.WriteByte(SSH_MSG_CHANNEL_OPEN_FAILURE); packet.WriteUINT32(remoteid); packet.WriteUINT32(ChannelOpenException.UNKNOWN_CHANNEL_TYPE); packet.WriteString(type + " is not a supported channel type!"); packet.WriteString(""); } #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_CHANNEL_OPEN_FAILURE"); System.Diagnostics.Trace.WriteLine("Remoteid=" + remoteid); System.Diagnostics.Trace.WriteLine("Name=" + type); #endif transport.SendMessage(packet); } catch (System.IO.IOException ex1) { throw new SSHException(ex1.Message, SSHException.INTERNAL_ERROR); } }