From 5b3a92ee6658b8917600a4fafe3667ccac6e4b9a Mon Sep 17 00:00:00 2001 From: antiduh Date: Thu, 26 Jun 2014 18:00:50 +0000 Subject: [PATCH] Implemented the server form. Client <-> Server interaction actually works. Fixed a few bugs in the CustomServer - wasn't configuring the listen socket correctly, doing read/write operations from the wrong socket, etc. Reworked how the CustomConnection class handles serialization. Added Disconnected events to the sample's CustomConnection and CustomServer classes so the forms could reset state. --- TestClient/Form1.Designer.cs | 1 + TestClient/Form1.cs | 26 +++- TestProtocol/CustomConnection.cs | 30 ++++- TestProtocol/CustomServer.cs | 34 ++++- TestServer/Form1.Designer.cs | 218 ++++++++++++++++++++++++++++++- TestServer/Form1.cs | 201 ++++++++++++++++++++++++++++ TestServer/Form1.resx | 120 +++++++++++++++++ TestServer/TestServer.csproj | 13 ++ 8 files changed, 626 insertions(+), 17 deletions(-) create mode 100644 TestServer/Form1.resx diff --git a/TestClient/Form1.Designer.cs b/TestClient/Form1.Designer.cs index 7b40a2c..5648f8d 100644 --- a/TestClient/Form1.Designer.cs +++ b/TestClient/Form1.Designer.cs @@ -75,6 +75,7 @@ this.serverTextBox.Name = "serverTextBox"; this.serverTextBox.Size = new System.Drawing.Size(137, 20); this.serverTextBox.TabIndex = 2; + this.serverTextBox.Text = "localhost"; // // portNumeric // diff --git a/TestClient/Form1.cs b/TestClient/Form1.cs index 51d9c6c..d93486a 100644 --- a/TestClient/Form1.cs +++ b/TestClient/Form1.cs @@ -40,14 +40,13 @@ namespace TestClient InitializeComponent(); this.connectButton.Click += connectButton_Click; - this.disconnectButton.Click += disconnectButton_Click; - this.encryptButton.Click += encryptButton_Click; - this.signButton.Click += signButton_Click; + this.FormClosing += Form1_FormClosing; + // --- SSPI --- this.cred = new ClientCredential( SecurityPackage.Negotiate ); @@ -64,6 +63,7 @@ namespace TestClient this.connection = new CustomConnection(); this.connection.Received += connection_Received; + this.connection.Disconnected += connection_Disconnected; // --- UI Fillout --- this.usernameTextbox.Text = this.cred.Name; @@ -71,6 +71,11 @@ namespace TestClient UpdateButtons(); } + private void Form1_FormClosing( object sender, FormClosingEventArgs e ) + { + this.connection.Stop(); + } + private void connectButton_Click( object sender, EventArgs e ) { if( string.IsNullOrWhiteSpace( this.serverTextBox.Text ) ) @@ -82,6 +87,8 @@ namespace TestClient try { this.connection.StartClient( this.serverTextBox.Text, (int)this.portNumeric.Value ); + this.initializing = true; + DoInit(); } catch( SocketException socketExcept ) { @@ -101,7 +108,6 @@ namespace TestClient } } - this.initializing = true; } } @@ -174,6 +180,18 @@ namespace TestClient } ); } + private void connection_Disconnected() + { + this.connected = false; + this.initializing = false; + this.lastServerToken = null; + + this.Invoke( (Action)delegate() + { + UpdateButtons(); + }); + } + private void DoInit() { SecurityStatus status; diff --git a/TestProtocol/CustomConnection.cs b/TestProtocol/CustomConnection.cs index 43b02ac..0822510 100644 --- a/TestProtocol/CustomConnection.cs +++ b/TestProtocol/CustomConnection.cs @@ -27,6 +27,8 @@ namespace TestProtocol public event ReceivedAction Received; + public event Action Disconnected; + public void StartClient( string server, int port ) { if( this.running ) @@ -49,7 +51,7 @@ namespace TestProtocol { if( this.running == false ) { - throw new InvalidOperationException( "Already stopped" ); + return; } this.socket.Close(); @@ -64,13 +66,19 @@ namespace TestProtocol } byte[] outBuffer = new byte[ message.Data.Length + 8 ]; + int position = 0; - ByteWriter.WriteInt32_BE( (int)message.Operation, outBuffer, 0 ); - ByteWriter.WriteInt32_BE( message.Data.Length, outBuffer, 4 ); + ByteWriter.WriteInt32_BE( (int)message.Operation, outBuffer, position ); + position += 4; - Array.Copy( message.Data, 0, outBuffer, 8, message.Data.Length ); + ByteWriter.WriteInt32_BE( message.Data.Length, outBuffer, position ); + position += 4; + + Array.Copy( message.Data, 0, outBuffer, position, message.Data.Length ); this.socket.Send( outBuffer, 0, outBuffer.Length, SocketFlags.None ); + + Console.Out.WriteLine( "Client: Sent " + message.Operation ); } private void ReceiveThreadEntry() @@ -82,7 +90,20 @@ namespace TestProtocol catch( Exception e ) { MessageBox.Show( "The SspiConnection receive thread crashed:\r\n\r\n" + e.ToString() ); + } + finally + { this.running = false; + + try + { + if( this.Disconnected != null ) + { + this.Disconnected(); + } + } + catch + { } } } @@ -133,6 +154,7 @@ namespace TestProtocol } } + Console.Out.WriteLine( "Client: Received " + operation ); if( this.Received != null ) { diff --git a/TestProtocol/CustomServer.cs b/TestProtocol/CustomServer.cs index 8493470..89ecf3b 100644 --- a/TestProtocol/CustomServer.cs +++ b/TestProtocol/CustomServer.cs @@ -9,6 +9,7 @@ namespace TestProtocol using System; using System.Collections.Generic; using System.Linq; + using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; @@ -35,6 +36,8 @@ namespace TestProtocol public event ReceivedAction Received; + public event Action Disconnected; + public void StartServer( int port ) { if( this.running ) @@ -43,6 +46,8 @@ namespace TestProtocol } this.serverSocket = new Socket( SocketType.Stream, ProtocolType.Tcp ); + this.serverSocket.Bind( new IPEndPoint( IPAddress.Any, port ) ); + this.serverSocket.Listen( 1 ); this.running = true; @@ -55,7 +60,7 @@ namespace TestProtocol { if( this.running == false ) { - throw new InvalidOperationException( "Already stopped" ); + return; } this.serverSocket.Close(); @@ -82,7 +87,9 @@ namespace TestProtocol Array.Copy( message.Data, 0, outBuffer, 8, message.Data.Length ); - this.serverSocket.Send( outBuffer, 0, outBuffer.Length, SocketFlags.None ); + this.readSocket.Send( outBuffer, 0, outBuffer.Length, SocketFlags.None ); + + Console.Out.WriteLine( "Server: Sent " + message.Operation ); } private void ReceiveThreadEntry() @@ -112,13 +119,25 @@ namespace TestProtocol } ReadLoop(); - } } catch( Exception e ) { MessageBox.Show( "The SspiConnection receive thread crashed:\r\n\r\n" + e.ToString() ); + } + finally + { this.running = false; + + try + { + if( this.Disconnected != null ) + { + this.Disconnected(); + } + } + catch + { } } } @@ -138,7 +157,7 @@ namespace TestProtocol // Read the operation. - this.serverSocket.Receive( readBuffer, 4, SocketFlags.None ); + this.readSocket.Receive( readBuffer, 4, SocketFlags.None ); // Check if we popped out of a receive call after we were shut down. if( this.running == false ) { break; } @@ -146,11 +165,11 @@ namespace TestProtocol operation = (ProtocolOp)ByteWriter.ReadInt32_BE( readBuffer, 0 ); // Read the length - this.serverSocket.Receive( readBuffer, 4, SocketFlags.None ); + this.readSocket.Receive( readBuffer, 4, SocketFlags.None ); length = ByteWriter.ReadInt32_BE( readBuffer, 0 ); // Read the data - this.serverSocket.Receive( readBuffer, length, SocketFlags.None ); + this.readSocket.Receive( readBuffer, length, SocketFlags.None ); } catch( SocketException e ) @@ -169,6 +188,7 @@ namespace TestProtocol } } + Console.Out.WriteLine( "Server: Received " + operation ); if( this.Received != null ) { @@ -180,7 +200,7 @@ namespace TestProtocol { this.Received( message ); } - catch( Exception e ) + catch( Exception ) { } } diff --git a/TestServer/Form1.Designer.cs b/TestServer/Form1.Designer.cs index fe5c6aa..98759ee 100644 --- a/TestServer/Form1.Designer.cs +++ b/TestServer/Form1.Designer.cs @@ -28,12 +28,226 @@ /// private void InitializeComponent() { - this.components = new System.ComponentModel.Container(); + this.label1 = new System.Windows.Forms.Label(); + this.portNumeric = new System.Windows.Forms.NumericUpDown(); + this.startButton = new System.Windows.Forms.Button(); + this.stopButton = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.usernameTextbox = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.sendTextbox = new System.Windows.Forms.TextBox(); + this.receivedTextbox = new System.Windows.Forms.TextBox(); + this.encryptButton = new System.Windows.Forms.Button(); + this.signButton = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.portNumeric)).BeginInit(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 21); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(83, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Port to listen on:"; + // + // portNumeric + // + this.portNumeric.Location = new System.Drawing.Point(101, 19); + this.portNumeric.Maximum = new decimal(new int[] { + 65536, + 0, + 0, + 0}); + this.portNumeric.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.portNumeric.Name = "portNumeric"; + this.portNumeric.Size = new System.Drawing.Size(69, 20); + this.portNumeric.TabIndex = 1; + this.portNumeric.Value = new decimal(new int[] { + 10000, + 0, + 0, + 0}); + // + // startButton + // + this.startButton.Location = new System.Drawing.Point(196, 16); + this.startButton.Name = "startButton"; + this.startButton.Size = new System.Drawing.Size(75, 23); + this.startButton.TabIndex = 2; + this.startButton.Text = "Start server"; + this.startButton.UseVisualStyleBackColor = true; + // + // stopButton + // + this.stopButton.Location = new System.Drawing.Point(277, 16); + this.stopButton.Name = "stopButton"; + this.stopButton.Size = new System.Drawing.Size(75, 23); + this.stopButton.TabIndex = 3; + this.stopButton.Text = "Stop server"; + this.stopButton.UseVisualStyleBackColor = true; + // + // groupBox1 + // + this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBox1.Controls.Add(this.label2); + this.groupBox1.Controls.Add(this.usernameTextbox); + this.groupBox1.Location = new System.Drawing.Point(400, 12); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(313, 65); + this.groupBox1.TabIndex = 4; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Server credentials"; + // + // usernameTextbox + // + this.usernameTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.usernameTextbox.Location = new System.Drawing.Point(74, 20); + this.usernameTextbox.Name = "usernameTextbox"; + this.usernameTextbox.ReadOnly = true; + this.usernameTextbox.Size = new System.Drawing.Size(233, 20); + this.usernameTextbox.TabIndex = 0; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(7, 23); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(61, 13); + this.label2.TabIndex = 1; + this.label2.Text = "User name:"; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.signButton); + this.groupBox2.Controls.Add(this.encryptButton); + this.groupBox2.Controls.Add(this.sendTextbox); + this.groupBox2.Dock = System.Windows.Forms.DockStyle.Fill; + this.groupBox2.Location = new System.Drawing.Point(3, 3); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(344, 381); + this.groupBox2.TabIndex = 5; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Send a message to the client"; + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.receivedTextbox); + this.groupBox3.Dock = System.Windows.Forms.DockStyle.Fill; + this.groupBox3.Location = new System.Drawing.Point(353, 3); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(345, 381); + this.groupBox3.TabIndex = 6; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "Received messages from the client"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.groupBox2, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.groupBox3, 1, 0); + this.tableLayoutPanel1.Location = new System.Drawing.Point(12, 87); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(701, 387); + this.tableLayoutPanel1.TabIndex = 7; + // + // sendTextbox + // + this.sendTextbox.Location = new System.Drawing.Point(6, 19); + this.sendTextbox.Multiline = true; + this.sendTextbox.Name = "sendTextbox"; + this.sendTextbox.Size = new System.Drawing.Size(331, 322); + this.sendTextbox.TabIndex = 0; + // + // receivedTextbox + // + this.receivedTextbox.Dock = System.Windows.Forms.DockStyle.Fill; + this.receivedTextbox.Location = new System.Drawing.Point(3, 16); + this.receivedTextbox.Multiline = true; + this.receivedTextbox.Name = "receivedTextbox"; + this.receivedTextbox.Size = new System.Drawing.Size(339, 362); + this.receivedTextbox.TabIndex = 0; + // + // encryptButton + // + this.encryptButton.Location = new System.Drawing.Point(33, 349); + this.encryptButton.Name = "encryptButton"; + this.encryptButton.Size = new System.Drawing.Size(120, 23); + this.encryptButton.TabIndex = 2; + this.encryptButton.Text = "Encrypt and send"; + this.encryptButton.UseVisualStyleBackColor = true; + // + // signButton + // + this.signButton.Location = new System.Drawing.Point(174, 349); + this.signButton.Name = "signButton"; + this.signButton.Size = new System.Drawing.Size(117, 23); + this.signButton.TabIndex = 3; + this.signButton.Text = "Sign and send"; + this.signButton.UseVisualStyleBackColor = true; + // + // Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Text = "Form1"; + this.ClientSize = new System.Drawing.Size(725, 477); + this.Controls.Add(this.tableLayoutPanel1); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.stopButton); + this.Controls.Add(this.startButton); + this.Controls.Add(this.portNumeric); + this.Controls.Add(this.label1); + this.Name = "Form1"; + this.Text = "Server - SSPI Test"; + ((System.ComponentModel.ISupportInitialize)(this.portNumeric)).EndInit(); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + } #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.NumericUpDown portNumeric; + private System.Windows.Forms.Button startButton; + private System.Windows.Forms.Button stopButton; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox usernameTextbox; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.Button signButton; + private System.Windows.Forms.Button encryptButton; + private System.Windows.Forms.TextBox sendTextbox; + private System.Windows.Forms.TextBox receivedTextbox; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; } } diff --git a/TestServer/Form1.cs b/TestServer/Form1.cs index d30bc15..6f8dee4 100644 --- a/TestServer/Form1.cs +++ b/TestServer/Form1.cs @@ -7,14 +7,215 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; +using TestProtocol; namespace TestServer { + using NSspi; + using NSspi.Contexts; + using NSspi.Credentials; + using Message = TestProtocol.Message; + public partial class Form1 : Form { + private ServerCredential serverCred; + + private ServerContext serverContext; + + private CustomServer server; + + private bool running; + + private bool initializing; + + private bool connected; + public Form1() { InitializeComponent(); + + this.serverCred = new ServerCredential( SecurityPackage.Negotiate ); + + this.serverContext = new ServerContext( + serverCred, + ContextAttrib.AcceptIntegrity | + ContextAttrib.ReplayDetect | + ContextAttrib.SequenceDetect | + ContextAttrib.MutualAuth | + ContextAttrib.Delegate | + ContextAttrib.Confidentiality + ); + + this.server = new CustomServer(); + this.server.Received += server_Received; + this.server.Disconnected += server_Disconnected; + + this.FormClosing += Form1_FormClosing; + + this.startButton.Click += startButton_Click; + this.stopButton.Click += stopButton_Click; + + this.encryptButton.Click += encryptButton_Click; + this.signButton.Click += signButton_Click; + + this.running = false; + this.initializing = false; + this.connected = false; + + UpdateButtons(); } + + private void Form1_FormClosing( object sender, FormClosingEventArgs e ) + { + this.server.Stop(); + } + + private void startButton_Click( object sender, EventArgs e ) + { + this.server.StartServer( (int)this.portNumeric.Value ); + + this.running = true; + this.initializing = true; + this.connected = false; + + UpdateButtons(); + } + + private void stopButton_Click( object sender, EventArgs e ) + { + this.server.Stop(); + + this.running = false; + this.initializing = false; + this.connected = false; + + UpdateButtons(); + } + + private void encryptButton_Click( object sender, EventArgs e ) + { + Message message; + + byte[] plainText = Encoding.UTF8.GetBytes( this.sendTextbox.Text ); + byte[] cipherText = this.serverContext.Encrypt( plainText ); + + message = new Message( ProtocolOp.EncryptedMessage, cipherText ); + + this.server.Send( message ); + } + + private void signButton_Click( object sender, EventArgs e ) + { + // Not implemented. + } + + private void UpdateButtons() + { + this.startButton.Enabled = this.running == false; + this.stopButton.Enabled = this.running; + + this.encryptButton.Enabled = this.connected; + this.signButton.Enabled = this.connected; + } + + + private void server_Received( Message message ) + { + if( message.Operation == ProtocolOp.ClientToken ) + { + HandleInit( message ); + } + else if( message.Operation == ProtocolOp.EncryptedMessage ) + { + HandleEncrypted( message ); + } + else if( message.Operation == ProtocolOp.SignedMessage ) + { + HandleSigned( message ); + } + else + { + HandleUnknown( message ); + } + } + + private void server_Disconnected() + { + this.running = false; + this.initializing = false; + this.connected = false; + + UpdateButtons(); + } + + + private void HandleInit( Message message ) + { + byte[] nextToken; + SecurityStatus status; + + if( initializing ) + { + status = this.serverContext.AcceptToken( message.Data, out nextToken ); + + if( status == SecurityStatus.OK || status == SecurityStatus.ContinueNeeded ) + { + if( nextToken != null ) + { + this.server.Send( new Message( ProtocolOp.ServerToken, nextToken ) ); + } + + if( status == SecurityStatus.OK ) + { + this.initializing = false; + this.connected = true; + + this.Invoke( (Action)delegate() { UpdateButtons(); } ); + } + } + else + { + this.Invoke( (Action)delegate() + { + MessageBox.Show( "Failed to accept token from client. Sspi error code: " + status ); + } ); + } + + } + } + + private void HandleEncrypted( Message message ) + { + this.Invoke( (Action)delegate() + { + byte[] plainText = this.serverContext.Decrypt( message.Data ); + string text = Encoding.UTF8.GetString( plainText ); + + this.receivedTextbox.Text += "Received encrypted message from client:\r\n" + text + "\r\n"; + } ); + } + + private void HandleSigned( Message message ) + { + // Not implemented yet. + /* + this.Invoke( (Action)delegate() + { + byte[] plainText = this.serverContext.Decrypt( message.Data ); + string text = Encoding.UTF8.GetString( plainText ); + + this.receivedTextbox.Text += "Received encrypted message from client:\r\n" + text + "\r\n"; + } ); + */ + } + + private void HandleUnknown( Message message ) + { + this.Invoke( (Action)delegate() + { + MessageBox.Show( "Received unexpected message from server. Message type: " + message.Operation ); + } ); + } + } } diff --git a/TestServer/Form1.resx b/TestServer/Form1.resx new file mode 100644 index 0000000..29dcb1b --- /dev/null +++ b/TestServer/Form1.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/TestServer/TestServer.csproj b/TestServer/TestServer.csproj index b9d20f1..ac5f941 100644 --- a/TestServer/TestServer.csproj +++ b/TestServer/TestServer.csproj @@ -52,6 +52,9 @@ + + Form1.cs + ResXFileCodeGenerator Resources.Designer.cs @@ -74,6 +77,16 @@ + + + {4b4cd933-bf62-4f92-b8fa-6771758c5197} + NSspi + + + {9bfd94e1-d9fb-44d7-a6e7-8bac2620e535} + TestProtocol + +