blob: ca8ee41a01ad4b77d0ac6058376d013114481aca [file] [log] [blame]
Jens Geyerc1d79432014-04-22 22:52:43 +02001/**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20using System;
21using System.Net.Security;
22using System.Net.Sockets;
23using System.Security.Authentication;
24using System.Security.Cryptography.X509Certificates;
25
26namespace Thrift.Transport
27{
Jens Geyerd5436f52014-10-03 19:50:38 +020028 /// <summary>
29 /// SSL Socket Wrapper class
30 /// </summary>
31 public class TTLSSocket : TStreamTransport
32 {
33 /// <summary>
34 /// Internal TCP Client
35 /// </summary>
36 private TcpClient client = null;
Jens Geyerc1d79432014-04-22 22:52:43 +020037
Jens Geyerd5436f52014-10-03 19:50:38 +020038 /// <summary>
39 /// The host
40 /// </summary>
41 private string host = null;
Jens Geyerc1d79432014-04-22 22:52:43 +020042
Jens Geyerd5436f52014-10-03 19:50:38 +020043 /// <summary>
44 /// The port
45 /// </summary>
46 private int port = 0;
Jens Geyerc1d79432014-04-22 22:52:43 +020047
Jens Geyerd5436f52014-10-03 19:50:38 +020048 /// <summary>
49 /// The timeout for the connection
50 /// </summary>
51 private int timeout = 0;
Jens Geyerc1d79432014-04-22 22:52:43 +020052
Jens Geyerd5436f52014-10-03 19:50:38 +020053 /// <summary>
54 /// Internal SSL Stream for IO
55 /// </summary>
56 private SslStream secureStream = null;
Jens Geyerc1d79432014-04-22 22:52:43 +020057
Jens Geyerd5436f52014-10-03 19:50:38 +020058 /// <summary>
59 /// Defines wheter or not this socket is a server socket<br/>
60 /// This is used for the TLS-authentication
61 /// </summary>
62 private bool isServer = false;
Jens Geyerc1d79432014-04-22 22:52:43 +020063
Jens Geyerd5436f52014-10-03 19:50:38 +020064 /// <summary>
65 /// The certificate
66 /// </summary>
67 private X509Certificate certificate = null;
Jens Geyerc1d79432014-04-22 22:52:43 +020068
Jens Geyerd5436f52014-10-03 19:50:38 +020069 /// <summary>
70 /// User defined certificate validator.
71 /// </summary>
72 private RemoteCertificateValidationCallback certValidator = null;
Jens Geyer7b11fec2014-06-05 22:03:19 +020073
Jens Geyerd5436f52014-10-03 19:50:38 +020074 /// <summary>
75 /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
76 /// </summary>
77 /// <param name="client">An already created TCP-client</param>
78 /// <param name="certificate">The certificate.</param>
79 /// <param name="isServer">if set to <c>true</c> [is server].</param>
Jens Geyeraf577242015-03-30 23:44:51 +020080 /// <param name="certValidator">User defined cert validator.</param>
81 public TTLSSocket(TcpClient client, X509Certificate certificate, bool isServer = false, RemoteCertificateValidationCallback certValidator = null)
Jens Geyerd5436f52014-10-03 19:50:38 +020082 {
83 this.client = client;
84 this.certificate = certificate;
Jens Geyeraf577242015-03-30 23:44:51 +020085 this.certValidator = certValidator;
Jens Geyerd5436f52014-10-03 19:50:38 +020086 this.isServer = isServer;
Jens Geyerc1d79432014-04-22 22:52:43 +020087
Jens Geyerd5436f52014-10-03 19:50:38 +020088 if (IsOpen)
89 {
90 base.inputStream = client.GetStream();
91 base.outputStream = client.GetStream();
92 }
93 }
Jens Geyerc1d79432014-04-22 22:52:43 +020094
Jens Geyerd5436f52014-10-03 19:50:38 +020095 /// <summary>
96 /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
97 /// </summary>
98 /// <param name="host">The host, where the socket should connect to.</param>
99 /// <param name="port">The port.</param>
100 /// <param name="certificatePath">The certificate path.</param>
101 /// <param name="certValidator">User defined cert validator.</param>
102 public TTLSSocket(string host, int port, string certificatePath, RemoteCertificateValidationCallback certValidator = null)
103 : this(host, port, 0, X509Certificate.CreateFromCertFile(certificatePath), certValidator)
104 {
105 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200106
Jens Geyerd5436f52014-10-03 19:50:38 +0200107 /// <summary>
108 /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
109 /// </summary>
110 /// <param name="host">The host, where the socket should connect to.</param>
111 /// <param name="port">The port.</param>
112 /// <param name="certificate">The certificate.</param>
113 /// <param name="certValidator">User defined cert validator.</param>
114 public TTLSSocket(string host, int port, X509Certificate certificate, RemoteCertificateValidationCallback certValidator = null)
115 : this(host, port, 0, certificate, certValidator)
116 {
117 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200118
Jens Geyerd5436f52014-10-03 19:50:38 +0200119 /// <summary>
120 /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
121 /// </summary>
122 /// <param name="host">The host, where the socket should connect to.</param>
123 /// <param name="port">The port.</param>
124 /// <param name="timeout">The timeout.</param>
125 /// <param name="certificate">The certificate.</param>
126 /// <param name="certValidator">User defined cert validator.</param>
127 public TTLSSocket(string host, int port, int timeout, X509Certificate certificate, RemoteCertificateValidationCallback certValidator = null)
128 {
129 this.host = host;
130 this.port = port;
131 this.timeout = timeout;
132 this.certificate = certificate;
133 this.certValidator = certValidator;
Jens Geyerc1d79432014-04-22 22:52:43 +0200134
Jens Geyerd5436f52014-10-03 19:50:38 +0200135 InitSocket();
136 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200137
Jens Geyerd5436f52014-10-03 19:50:38 +0200138 /// <summary>
139 /// Creates the TcpClient and sets the timeouts
140 /// </summary>
141 private void InitSocket()
142 {
143 this.client = new TcpClient();
144 client.ReceiveTimeout = client.SendTimeout = timeout;
145 client.Client.NoDelay = true;
146 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200147
Jens Geyerd5436f52014-10-03 19:50:38 +0200148 /// <summary>
149 /// Sets Send / Recv Timeout for IO
150 /// </summary>
151 public int Timeout
152 {
153 set
154 {
155 this.client.ReceiveTimeout = this.client.SendTimeout = this.timeout = value;
156 }
157 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200158
Jens Geyerd5436f52014-10-03 19:50:38 +0200159 /// <summary>
160 /// Gets the TCP client.
161 /// </summary>
162 public TcpClient TcpClient
163 {
164 get
165 {
166 return client;
167 }
168 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200169
Jens Geyerd5436f52014-10-03 19:50:38 +0200170 /// <summary>
171 /// Gets the host.
172 /// </summary>
173 public string Host
174 {
175 get
176 {
177 return host;
178 }
179 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200180
Jens Geyerd5436f52014-10-03 19:50:38 +0200181 /// <summary>
182 /// Gets the port.
183 /// </summary>
184 public int Port
185 {
186 get
187 {
188 return port;
189 }
190 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200191
Jens Geyerd5436f52014-10-03 19:50:38 +0200192 /// <summary>
193 /// Gets a value indicating whether TCP Client is Cpen
194 /// </summary>
195 public override bool IsOpen
196 {
197 get
198 {
199 if (this.client == null)
200 {
201 return false;
202 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200203
Jens Geyerd5436f52014-10-03 19:50:38 +0200204 return this.client.Connected;
205 }
206 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200207
Jens Geyerd5436f52014-10-03 19:50:38 +0200208 /// <summary>
209 /// Validates the certificates!<br/>
210 /// </summary>
211 /// <param name="sender">The sender-object.</param>
212 /// <param name="certificate">The used certificate.</param>
213 /// <param name="chain">The certificate chain.</param>
214 /// <param name="sslPolicyErrors">An enum, which lists all the errors from the .NET certificate check.</param>
215 /// <returns></returns>
216 private bool CertificateValidator(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslValidationErrors)
217 {
218 return (sslValidationErrors == SslPolicyErrors.None);
219 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200220
Jens Geyerd5436f52014-10-03 19:50:38 +0200221 /// <summary>
222 /// Connects to the host and starts the routine, which sets up the TLS
223 /// </summary>
224 public override void Open()
225 {
226 if (IsOpen)
227 {
228 throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected");
229 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200230
Jens Geyerd5436f52014-10-03 19:50:38 +0200231 if (String.IsNullOrEmpty(host))
232 {
233 throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host");
234 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200235
Jens Geyerd5436f52014-10-03 19:50:38 +0200236 if (port <= 0)
237 {
238 throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port");
239 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200240
Jens Geyerd5436f52014-10-03 19:50:38 +0200241 if (client == null)
242 {
243 InitSocket();
244 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200245
Jens Geyerd5436f52014-10-03 19:50:38 +0200246 client.Connect(host, port);
Jens Geyerc1d79432014-04-22 22:52:43 +0200247
Jens Geyerd5436f52014-10-03 19:50:38 +0200248 setupTLS();
249 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200250
Jens Geyerd5436f52014-10-03 19:50:38 +0200251 /// <summary>
252 /// Creates a TLS-stream and lays it over the existing socket
253 /// </summary>
254 public void setupTLS()
255 {
Jens Geyeraf577242015-03-30 23:44:51 +0200256 this.secureStream = new SslStream(this.client.GetStream(), false, this.certValidator ?? CertificateValidator);
Jens Geyerd5436f52014-10-03 19:50:38 +0200257 if (isServer)
258 {
259 // Server authentication
Jens Geyeraf577242015-03-30 23:44:51 +0200260 this.secureStream.AuthenticateAsServer(this.certificate, this.certValidator != null, SslProtocols.Tls, true);
Jens Geyerd5436f52014-10-03 19:50:38 +0200261 }
262 else
263 {
264 // Client authentication
Jens Geyeraf577242015-03-30 23:44:51 +0200265 this.secureStream.AuthenticateAsClient(host, new X509CertificateCollection { certificate }, SslProtocols.Tls, true);
Jens Geyerd5436f52014-10-03 19:50:38 +0200266 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200267
Jens Geyerd5436f52014-10-03 19:50:38 +0200268 inputStream = this.secureStream;
269 outputStream = this.secureStream;
270 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200271
Jens Geyerd5436f52014-10-03 19:50:38 +0200272 /// <summary>
273 /// Closes the SSL Socket
274 /// </summary>
275 public override void Close()
276 {
277 base.Close();
278 if (this.client != null)
279 {
280 this.client.Close();
281 this.client = null;
282 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200283
Jens Geyerd5436f52014-10-03 19:50:38 +0200284 if (this.secureStream != null)
285 {
286 this.secureStream.Close();
287 this.secureStream = null;
288 }
289 }
290 }
Jens Geyerc1d79432014-04-22 22:52:43 +0200291}