2008-11-01 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Security.Tokens / SpnegoSecurityTokenAuthenticator.cs
1 //
2 // SpnegoSecurityTokenAuthenticator.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2007 Novell, Inc.  http://www.novell.com
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Collections.Generic;
30 using System.Collections.ObjectModel;
31 using System.IO;
32 using System.Net.Security;
33 using System.IdentityModel.Policy;
34 using System.IdentityModel.Selectors;
35 using System.IdentityModel.Tokens;
36 using System.Security.Cryptography;
37 using System.Security.Cryptography.X509Certificates;
38 using System.Security.Cryptography.Xml;
39 using System.ServiceModel.Channels;
40 using System.ServiceModel.Description;
41 using System.ServiceModel.Security;
42 using System.ServiceModel.Security.Tokens;
43 using System.Text;
44 using System.Xml;
45 using Mono.Security;
46
47 using ReqType = System.ServiceModel.Security.Tokens.ServiceModelSecurityTokenRequirement;
48
49 namespace System.ServiceModel.Security.Tokens
50 {
51         // FIXME: implement all
52         class SpnegoSecurityTokenAuthenticator : CommunicationSecurityTokenAuthenticator
53         {
54                 ServiceCredentialsSecurityTokenManager manager;
55                 SpnegoAuthenticatorCommunicationObject comm;
56
57                 public SpnegoSecurityTokenAuthenticator (
58                         ServiceCredentialsSecurityTokenManager manager, 
59                         SecurityTokenRequirement r)
60                 {
61                         this.manager = manager;
62                         comm = new SpnegoAuthenticatorCommunicationObject (this);
63                 }
64
65                 public ServiceCredentialsSecurityTokenManager Manager {
66                         get { return manager; }
67                 }
68
69                 public override AuthenticatorCommunicationObject Communication {
70                         get { return comm; }
71                 }
72
73                 protected override bool CanValidateTokenCore (SecurityToken token)
74                 {
75                         throw new NotImplementedException ();
76                 }
77
78                 protected override ReadOnlyCollection<IAuthorizationPolicy>
79                         ValidateTokenCore (SecurityToken token)
80                 {
81                         throw new NotImplementedException ();
82                 }
83         }
84
85         class SpnegoAuthenticatorCommunicationObject : AuthenticatorCommunicationObject
86         {
87                 SpnegoSecurityTokenAuthenticator owner;
88
89                 public SpnegoAuthenticatorCommunicationObject (SpnegoSecurityTokenAuthenticator owner)
90                 {
91                         this.owner = owner;
92                 }
93
94                 WSTrustSecurityTokenServiceProxy proxy;
95
96                 protected internal override TimeSpan DefaultCloseTimeout {
97                         get { throw new NotImplementedException (); }
98                 }
99
100                 protected internal override TimeSpan DefaultOpenTimeout {
101                         get { throw new NotImplementedException (); }
102                 }
103
104                 public override Message ProcessNegotiation (Message request)
105                 {
106                         if (request.Headers.Action == Constants.WstIssueAction)
107                                 return ProcessMessageType1 (request);
108                         else
109                                 return ProcessMessageType3 (request);
110                 }
111
112                 class TlsServerSessionInfo
113                 {
114                         public TlsServerSessionInfo (string context, TlsServerSession tls)
115                         {
116                                 ContextId = context;
117                                 Tls = tls;
118                         }
119
120                         public string ContextId;
121                         public TlsServerSession Tls;
122                         public MemoryStream Messages = new MemoryStream ();
123                 }
124
125                 Dictionary<string,SspiServerSession> sessions =
126                         new Dictionary<string,SspiServerSession> ();
127
128                 void AppendNegotiationMessageXml (XmlReader reader, TlsServerSessionInfo tlsInfo)
129                 {
130                         XmlDsigExcC14NTransform t = new XmlDsigExcC14NTransform ();
131                         XmlDocument doc = new XmlDocument ();
132                         doc.PreserveWhitespace = true;
133                         reader.MoveToContent ();
134                         doc.AppendChild (doc.ReadNode (reader));
135                         t.LoadInput (doc);
136                         MemoryStream stream = (MemoryStream) t.GetOutput ();
137                         byte [] bytes = stream.ToArray ();
138                         tlsInfo.Messages.Write (bytes, 0, bytes.Length);
139                 }
140
141                 Message ProcessMessageType1 (Message request)
142                 {
143                         // FIXME: use correct buffer size
144                         MessageBuffer buffer = request.CreateBufferedCopy (0x10000);
145                         WSTrustRequestSecurityTokenReader reader =
146                                 new WSTrustRequestSecurityTokenReader (buffer.CreateMessage ().GetReaderAtBodyContents (), SecurityTokenSerializer);
147                         reader.Read ();
148
149                         if (sessions.ContainsKey (reader.Value.Context))
150                                 throw new SecurityNegotiationException (String.Format ("The context '{0}' already exists in this SSL negotiation manager", reader.Value.Context));
151
152 Console.WriteLine (buffer.CreateMessage ());
153
154                         SspiServerSession sspi = new SspiServerSession ();
155 //                      AppendNegotiationMessageXml (buffer.CreateMessage ().GetReaderAtBodyContents (), tlsInfo);
156
157                         // FIXME: when an explicit endpoint identity is
158                         // specified in the target EndpointAddress at client,
159                         // it sends some other kind of binary octets that
160                         // include NTLM octet, instead of raw NTLM octet itself.
161
162                         byte [] raw = reader.Value.BinaryExchange.Value;
163
164                         bool gss = "NTLMSSP" != Encoding.ASCII.GetString (raw, 0, 7);
165
166                         if (gss)
167                                 sspi.ProcessSpnegoInitialContextTokenRequest (raw);
168                         else
169                                 sspi.ProcessMessageType1 (raw);
170
171                         WstRequestSecurityTokenResponse rstr =
172                                 new WstRequestSecurityTokenResponse (SecurityTokenSerializer);
173                         rstr.Context = reader.Value.Context;
174                         rstr.BinaryExchange = new WstBinaryExchange (Constants.WstBinaryExchangeValueGss);
175
176                         if (gss)
177                                 rstr.BinaryExchange.Value = sspi.ProcessSpnegoInitialContextTokenResponse ();
178                         else
179                                 rstr.BinaryExchange.Value = sspi.ProcessMessageType2 ();
180
181                         Message reply = Message.CreateMessage (request.Version, Constants.WstIssueReplyAction, rstr);
182                         reply.Headers.RelatesTo = request.Headers.MessageId;
183
184                         // FIXME: use correct buffer size
185                         buffer = reply.CreateBufferedCopy (0x10000);
186 //                      AppendNegotiationMessageXml (buffer.CreateMessage ().GetReaderAtBodyContents (), tlsInfo);
187
188                         sessions [reader.Value.Context] = sspi;
189
190                         return buffer.CreateMessage ();
191                 }
192
193                 Message ProcessMessageType3 (Message request)
194                 {
195                         // FIXME: use correct buffer size
196                         MessageBuffer buffer = request.CreateBufferedCopy (0x10000);
197 Console.WriteLine (buffer.CreateMessage ());
198                         WSTrustRequestSecurityTokenResponseReader reader =
199                                 new WSTrustRequestSecurityTokenResponseReader (Constants.WstSpnegoProofTokenType, buffer.CreateMessage ().GetReaderAtBodyContents (), SecurityTokenSerializer, null);
200                         reader.Read ();
201
202                         byte [] raw = reader.Value.BinaryExchange.Value;
203
204                         bool gss = "NTLMSSP" != Encoding.ASCII.GetString (raw, 0, 7);
205
206 foreach (byte b in raw) Console.Write ("{0:X02} ", b); Console.WriteLine ();
207
208                         SspiServerSession sspi;
209                         if (!sessions.TryGetValue (reader.Value.Context, out sspi))
210                                 throw new SecurityNegotiationException (String.Format ("The context '{0}' does not exist in this SSL negotiation manager", reader.Value.Context));
211
212                         if (gss)
213                                 sspi.ProcessSpnegoProcessContextToken (raw);
214                         else
215                                 sspi.ProcessMessageType3 (raw);
216
217                         throw new NotImplementedException ();
218 /*
219                         AppendNegotiationMessageXml (buffer.CreateMessage ().GetReaderAtBodyContents (), tlsInfo);
220 //Console.WriteLine (System.Text.Encoding.UTF8.GetString (tlsInfo.Messages.ToArray ()));
221
222                         tls.ProcessClientKeyExchange (reader.Value.BinaryExchange.Value);
223
224                         byte [] serverFinished = tls.ProcessServerFinished ();
225
226                         // The shared key is computed as recommended in WS-Trust:
227                         // P_SHA1(encrypted_key,SHA1(exc14n(RST..RSTRs))+"CK-HASH")
228                         byte [] hash = SHA1.Create ().ComputeHash (tlsInfo.Messages.ToArray ());
229                         byte [] key = tls.CreateHash (tls.MasterSecret, hash, "CK-HASH");
230 foreach (byte b in hash) Console.Write ("{0:X02} ", b); Console.WriteLine ();
231 foreach (byte b in key) Console.Write ("{0:X02} ", b); Console.WriteLine ();
232
233                         WstRequestSecurityTokenResponseCollection col =
234                                 new WstRequestSecurityTokenResponseCollection ();
235                         WstRequestSecurityTokenResponse rstr =
236                                 new WstRequestSecurityTokenResponse (SecurityTokenSerializer);
237                         rstr.Context = reader.Value.Context;
238                         rstr.TokenType = Constants.WsscContextToken;
239                         DateTime from = DateTime.Now;
240                         // FIXME: not sure if arbitrary key is used here.
241                         SecurityContextSecurityToken sct = SecurityContextSecurityToken.CreateCookieSecurityContextToken (
242                                 // Create a new context.
243                                 // (do not use sslnego context here.)
244                                 new UniqueId (),
245                                 "uuid-" + Guid.NewGuid (),
246                                 key,
247                                 from,
248                                 // FIXME: use LocalServiceSecuritySettings.NegotiationTimeout
249                                 from.AddHours (8),
250                                 null,
251                                 owner.Manager.ServiceCredentials.SecureConversationAuthentication.SecurityStateEncoder);
252                         rstr.RequestedSecurityToken = sct;
253                         rstr.RequestedProofToken = tls.ProcessApplicationData (key);
254                         rstr.RequestedAttachedReference = new LocalIdKeyIdentifierClause (sct.Id);
255                         rstr.RequestedUnattachedReference = new SecurityContextKeyIdentifierClause (sct.ContextId, null);
256                         WstLifetime lt = new WstLifetime ();
257                         lt.Created = from;
258                         // FIXME: use LocalServiceSecuritySettings.NegotiationTimeout
259                         lt.Expires = from.AddHours (8);
260                         rstr.Lifetime = lt;
261                         rstr.BinaryExchange = new WstBinaryExchange (Constants.WstBinaryExchangeValueGss);
262                         rstr.BinaryExchange.Value = serverFinished;
263
264                         col.Responses.Add (rstr);
265
266                         // Authenticator is mandatory for MS sslnego.
267                         rstr = new WstRequestSecurityTokenResponse (SecurityTokenSerializer);
268                         rstr.Context = reader.Value.Context;
269                         rstr.Authenticator = tls.CreateHash (key, hash, "AUTH-HASH");
270                         col.Responses.Add (rstr);
271
272                         sessions.Remove (reader.Value.Context);
273
274                         return Message.CreateMessage (request.Version, Constants.WstIssueReplyAction, col);
275 */
276                 }
277
278                 protected override void OnAbort ()
279                 {
280                         throw new NotImplementedException ();
281                 }
282
283                 protected override void OnOpen (TimeSpan timeout)
284                 {
285                         if (State == CommunicationState.Opened)
286                                 throw new InvalidOperationException ("Already opened.");
287
288                         EnsureProperties ();
289
290                         proxy = new WSTrustSecurityTokenServiceProxy (
291                                 IssuerBinding, IssuerAddress);
292                 }
293
294                 protected override IAsyncResult OnBeginOpen (TimeSpan timeout, AsyncCallback callback, object state)
295                 {
296                         throw new NotImplementedException ();
297                 }
298
299                 protected override void OnEndOpen (IAsyncResult result)
300                 {
301                         throw new NotImplementedException ();
302                 }
303
304                 protected override void OnClose (TimeSpan timeout)
305                 {
306                         if (proxy != null)
307                                 proxy.Close ();
308                 }
309
310                 protected override IAsyncResult OnBeginClose (TimeSpan timeout, AsyncCallback callback, object state)
311                 {
312                         throw new NotImplementedException ();
313                 }
314
315                 protected override void OnEndClose (IAsyncResult result)
316                 {
317                         throw new NotImplementedException ();
318                 }
319         }
320 }