/// <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 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 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); } }
/// <summary> /// Opens a channel /// </summary> /// <param name="channel"></param> /// <param name="requestdata"></param> public void OpenChannel(SSH2Channel channel, byte[] requestdata) { lock (this) { try { int channelid = AllocateChannel(channel); if (channelid == -1) { throw new ChannelOpenException("Maximum number of channels exceeded", ChannelOpenException.RESOURCE_SHORTAGE); } channel.Init(this, channelid); SSHPacket packet = GetPacket(); packet.WriteByte(SSH_MSG_CHANNEL_OPEN); packet.WriteString(channel.Name); packet.WriteUINT32(channel.ChannelID); packet.WriteUINT32(channel.WindowSize); packet.WriteUINT32(channel.PacketSize); if (requestdata != null) { packet.WriteBytes(requestdata); } #if DEBUG System.Diagnostics.Trace.WriteLine("Sending SSH_MSG_CHANNEL_OPEN"); System.Diagnostics.Trace.WriteLine("Channelid=" + channel.ChannelID); System.Diagnostics.Trace.WriteLine("Name=" + channel.Name); #endif transport.SendMessage(packet); packet = channel.MessageStore.NextMessage(CHANNEL_OPEN_RESPONSE_MESSAGES); #if DEBUG System.Diagnostics.Trace.WriteLine("Received reply to SSH_MSG_CHANNEL_OPEN"); channel.LogMessage(packet); #endif if (packet.MessageID == SSH_MSG_CHANNEL_OPEN_FAILURE) { FreeChannel(channel); int reason = (int)packet.ReadUINT32(); throw new ChannelOpenException(packet.ReadString(), reason); } else { int remoteid = (int)packet.ReadUINT32(); int remotewindow = (int)packet.ReadUINT32(); int remotepacket = (int)packet.ReadUINT32(); byte[] responsedata = new byte[packet.Available]; packet.ReadBytes(responsedata); channel.Open(remoteid, remotewindow, remotepacket, responsedata); return; } } catch (System.IO.IOException ex) { throw new SSHException(ex.Message, SSHException.INTERNAL_ERROR); } } }
/// <summary> /// Start the authentication request. /// </summary> /// <param name="authentication">The authentication protocol instance.</param> /// <param name="serviceName">The name of the service to start upon a successful authentication.</param> public void Authenticate(AuthenticationProtocol authentication, String serviceName) { if (InteractivePrompt == null) { throw new SSHException("An interactive prompt event must be set!", SSHException.BAD_API_USAGE); } ByteBuffer baw = new ByteBuffer(); baw.WriteString(""); baw.WriteString(""); authentication.SendRequest(username, serviceName, "keyboard-interactive", baw.ToByteArray()); while (true) { SSHPacket msg = authentication.ReadMessage(); if (msg.MessageID != SSH_MSG_USERAUTH_INFO_REQUEST) { authentication.transport.Disconnect("Unexpected authentication message received!", DisconnectionReason.PROTOCOL_ERROR); throw new SSHException("Unexpected authentication message received!", SSHException.PROTOCOL_VIOLATION); } String name = msg.ReadString(); String instruction = msg.ReadString(); String langtag = msg.ReadString(); int num = (int)msg.ReadInt(); String prompt; bool echo; KBIPrompt[] prompts = new KBIPrompt[num]; for (int i = 0; i < num; i++) { prompt = msg.ReadString(); echo = (msg.ReadBool()); prompts[i] = new KBIPrompt(prompt, echo); } if (InteractivePrompt(name, instruction, prompts)) { msg = authentication.transport.GetSSHPacket(true); msg.WriteByte(SSH_MSG_USERAUTH_INFO_RESPONSE); msg.WriteInt(prompts.Length); for (int i = 0; i < prompts.Length; i++) { msg.WriteString(prompts[i].Response); } authentication.transport.SendMessage(msg); } else { throw new SSHException("User cancelled during authentication", SSHException.USER_CANCELATION); } } }