/// <summary> /// Connect to an SSH server. This method is not designed to be called directly but from /// an <see cref="Maverick.SSH.SSHConnector"/> instance. /// </summary> /// <param name="io">The underlying transport mechanism</param> /// <param name="context">The configuration context for this connection</param> /// <param name="connector">The connector creating this client</param> /// <param name="username">The username for the connection</param> /// <param name="localIdentification">The identification string sent to the server</param> /// <param name="remoteIdentification">The identification string received from the server</param> /// <param name="threaded">Should this connection use an additional thread to buffer messages</param> public void Connect(SSHTransport io, SSHContext context, SSHConnector connector, System.String username, System.String localIdentification, System.String remoteIdentification, bool threaded) { FireEvent(SSHState.CONNECTING); this.context = (SSH2Context)context; this.con = connector; this.io = io; this.threaded = threaded; this.username = username; this.localIdentification = localIdentification; this.remoteIdentification = remoteIdentification; this.transport = new TransportProtocol(io, this); this.transport.StateChange += new TransportProtocolStateChangeEvent(this.OnTransportEvent); this.transport.StartProtocol(); FireEvent(SSHState.CONNECTED); this.connection = new ConnectionProtocol(transport, threaded); this.connection.AddChannelFactory(requestFactory); this.authentication = new AuthenticationProtocol(transport, connection); RequestAuthenticationMethods(); }
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 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; } }