merge -r 60814:60815
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Tls / SslServerStream.cs
1 // Transport Security Layer (TLS)
2 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
3
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
12 // 
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 // 
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24
25 using System;
26 using System.Collections;
27 using System.IO;
28 using System.Net;
29 using System.Net.Sockets;
30 using System.Security.Cryptography;
31 using System.Security.Cryptography.X509Certificates;
32
33 using Mono.Security.Protocol.Tls.Handshake;
34
35 namespace Mono.Security.Protocol.Tls
36 {
37         public class SslServerStream : SslStreamBase
38         {
39                 #region Internal Events
40                 
41                 internal event CertificateValidationCallback    ClientCertValidation;
42                 internal event PrivateKeySelectionCallback              PrivateKeySelection;
43                 
44                 #endregion
45
46                 #region Properties
47
48                 public X509Certificate ClientCertificate
49                 {
50                         get
51                         {
52                                 if (this.context.HandshakeState == HandshakeState.Finished)
53                                 {
54                                         return this.context.ClientSettings.ClientCertificate;
55                                 }
56
57                                 return null;
58                         }
59                 }
60
61                 #endregion
62
63                 #region Callback Properties
64
65                 public CertificateValidationCallback ClientCertValidationDelegate 
66                 {
67                         get { return this.ClientCertValidation; }
68                         set { this.ClientCertValidation = value; }
69                 }
70
71                 public PrivateKeySelectionCallback PrivateKeyCertSelectionDelegate
72                 {
73                         get { return this.PrivateKeySelection; }
74                         set { this.PrivateKeySelection = value; }
75                 }
76
77                 #endregion
78
79                 #region Constructors
80
81                 public SslServerStream(
82                         Stream                  stream, 
83                         X509Certificate serverCertificate) : this(
84                         stream, 
85                         serverCertificate, 
86                         false, 
87                         false, 
88                         SecurityProtocolType.Default)
89                 {
90                 }
91
92                 public SslServerStream(
93                         Stream                  stream,
94                         X509Certificate serverCertificate,
95                         bool                    clientCertificateRequired,
96                         bool                    ownsStream): this(
97                         stream, 
98                         serverCertificate, 
99                         clientCertificateRequired, 
100                         ownsStream, 
101                         SecurityProtocolType.Default)
102                 {
103                 }
104
105                 public SslServerStream(
106                         Stream                                  stream,
107                         X509Certificate                 serverCertificate,
108                         bool                                    clientCertificateRequired,
109                         bool                                    ownsStream,
110                         SecurityProtocolType    securityProtocolType)
111                         : base(stream, ownsStream)
112                 {
113                         this.context = new ServerContext(
114                                 this,
115                                 securityProtocolType,
116                                 serverCertificate,
117                                 clientCertificateRequired);
118
119                         this.protocol = new ServerRecordProtocol(innerStream, (ServerContext)this.context);
120                 }
121
122                 #endregion
123
124                 #region Finalizer
125
126                 ~SslServerStream()
127                 {
128                         this.Dispose(false);
129                 }
130
131                 #endregion
132
133                 #region IDisposable Methods
134
135                 protected override void Dispose(bool disposing)
136                 {
137                         base.Dispose(disposing);
138
139                         if (disposing)
140                         {
141                                 this.ClientCertValidation = null;
142                                 this.PrivateKeySelection = null;
143                         }
144                 }
145
146                 #endregion
147
148                 #region Handsake Methods
149
150                 /*
151                         Client                                                                                  Server
152
153                         ClientHello                 -------->
154                                                                                                                         ServerHello
155                                                                                                                         Certificate*
156                                                                                                                         ServerKeyExchange*
157                                                                                                                         CertificateRequest*
158                                                                                 <--------                       ServerHelloDone
159                         Certificate*
160                         ClientKeyExchange
161                         CertificateVerify*
162                         [ChangeCipherSpec]
163                         Finished                    -------->
164                                                                                                                         [ChangeCipherSpec]
165                                                                                 <--------           Finished
166                         Application Data            <------->                   Application Data
167
168                                         Fig. 1 - Message flow for a full handshake              
169                 */
170
171                 internal override IAsyncResult OnBeginNegotiateHandshake(AsyncCallback callback, object state)
172                 {
173                         // Reset the context if needed
174                         if (this.context.HandshakeState != HandshakeState.None)
175                         {
176                                 this.context.Clear();
177                         }
178
179                         // Obtain supported cipher suites
180                         this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(this.context.SecurityProtocol);
181
182                         // Set handshake state
183                         this.context.HandshakeState = HandshakeState.Started;
184
185                         // Receive Client Hello message
186                         return this.protocol.BeginReceiveRecord(this.innerStream, callback, state);
187
188                 }
189
190                 internal override void OnNegotiateHandshakeCallback(IAsyncResult asyncResult)
191                 {
192                         // Receive Client Hello message and ignore it
193                         this.protocol.EndReceiveRecord(asyncResult);
194
195                         // If received message is not an ClientHello send a
196                         // Fatal Alert
197                         if (this.context.LastHandshakeMsg != HandshakeType.ClientHello)
198                         {
199                                 this.protocol.SendAlert(AlertDescription.UnexpectedMessage);
200                         }
201
202                         // Send ServerHello message
203                         this.protocol.SendRecord(HandshakeType.ServerHello);
204
205                         // Send ServerCertificate message
206                         this.protocol.SendRecord(HandshakeType.Certificate);
207
208                         // If the negotiated cipher is a KeyEx cipher send ServerKeyExchange
209                         if (this.context.Cipher.IsExportable)
210                         {
211                                 this.protocol.SendRecord(HandshakeType.ServerKeyExchange);
212                         }
213
214                         bool certRequested = false;
215
216                         // If the negotiated cipher is a KeyEx cipher or
217                         // the client certificate is required send the CertificateRequest message
218                         if (this.context.Cipher.IsExportable ||
219                                 ((ServerContext)this.context).ClientCertificateRequired)
220                         {
221                                 this.protocol.SendRecord(HandshakeType.CertificateRequest);
222                                 certRequested = true;
223                         }
224
225                         // Send ServerHelloDone message
226                         this.protocol.SendRecord(HandshakeType.ServerHelloDone);
227
228                         // Receive client response, until the Client Finished message
229                         // is received. IE can be interrupted at this stage and never
230                         // complete the handshake
231                         DateTime complete = DateTime.Now.AddSeconds (10);
232                         while (this.context.LastHandshakeMsg != HandshakeType.Finished)
233                         {
234                                 byte[] record = this.protocol.ReceiveRecord(this.innerStream);
235                                 if ((record == null) || (record.Length == 0))
236                                 {
237                                         throw new TlsException(
238                                                 AlertDescription.HandshakeFailiure,
239                                                 "The client stopped the handshake.");
240                                 }
241                         }
242
243                         if (certRequested && (this.context.ClientSettings.ClientCertificate == null))
244                         {
245                                 // we asked for a certificate but didn't receive one
246                                 // e.g. wget for SSL3
247                                 if (!RaiseClientCertificateValidation(null, new int[0]))
248                                 {
249                                         throw new TlsException(
250                                                 AlertDescription.BadCertificate,
251                                                 "No certificate received from client.");
252                                 }
253                         }
254
255                         // Send ChangeCipherSpec and ServerFinished messages
256                         this.protocol.SendChangeCipherSpec();
257                         this.protocol.SendRecord (HandshakeType.Finished);
258
259                         // The handshake is finished
260                         this.context.HandshakeState = HandshakeState.Finished;
261
262                         // Reset Handshake messages information
263                         this.context.HandshakeMessages.Reset ();
264
265                         // Clear Key Info
266                         this.context.ClearKeyInfo();
267                 }
268
269                 #endregion
270
271                 #region Event Methods
272
273                 internal override X509Certificate OnLocalCertificateSelection(X509CertificateCollection clientCertificates, X509Certificate serverCertificate, string targetHost, X509CertificateCollection serverRequestedCertificates)
274                 {
275                         throw new NotSupportedException();
276                 }
277
278                 internal override bool OnRemoteCertificateValidation(X509Certificate certificate, int[] errors)
279                 {
280                         if (this.ClientCertValidation != null)
281                         {
282                                 return this.ClientCertValidation(certificate, errors);
283                         }
284
285                         return (errors != null && errors.Length == 0);
286                 }
287
288                 internal bool RaiseClientCertificateValidation(
289                         X509Certificate certificate, 
290                         int[]                   certificateErrors)
291                 {
292                         return base.RaiseRemoteCertificateValidation(certificate, certificateErrors);
293                 }
294
295                 internal override AsymmetricAlgorithm OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost)
296                 {
297                         if (this.PrivateKeySelection != null)
298                         {
299                                 return this.PrivateKeySelection(certificate, targetHost);
300                         }
301
302                         return null;
303                 }
304
305                 internal AsymmetricAlgorithm RaisePrivateKeySelection(
306                         X509Certificate certificate,
307                         string targetHost)
308                 {
309                         return base.RaiseLocalPrivateKeySelection(certificate, targetHost);
310                 }
311
312                 #endregion
313         }
314 }