2010-06-03 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.ServiceModel / System.ServiceModel.Security.Tokens / SpnegoSecurityTokenAuthenticator.cs
bloba98ba30c6616e5473af241d90d58259a2ceda994
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.
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;
47 using ReqType = System.ServiceModel.Security.Tokens.ServiceModelSecurityTokenRequirement;
49 namespace System.ServiceModel.Security.Tokens
51 // FIXME: implement all
52 class SpnegoSecurityTokenAuthenticator : CommunicationSecurityTokenAuthenticator
54 ServiceCredentialsSecurityTokenManager manager;
55 SpnegoAuthenticatorCommunicationObject comm;
57 public SpnegoSecurityTokenAuthenticator (
58 ServiceCredentialsSecurityTokenManager manager,
59 SecurityTokenRequirement r)
61 this.manager = manager;
62 comm = new SpnegoAuthenticatorCommunicationObject (this);
65 public ServiceCredentialsSecurityTokenManager Manager {
66 get { return manager; }
69 public override AuthenticatorCommunicationObject Communication {
70 get { return comm; }
73 protected override bool CanValidateTokenCore (SecurityToken token)
75 throw new NotImplementedException ();
78 protected override ReadOnlyCollection<IAuthorizationPolicy>
79 ValidateTokenCore (SecurityToken token)
81 throw new NotImplementedException ();
85 class SpnegoAuthenticatorCommunicationObject : AuthenticatorCommunicationObject
87 SpnegoSecurityTokenAuthenticator owner;
89 public SpnegoAuthenticatorCommunicationObject (SpnegoSecurityTokenAuthenticator owner)
91 this.owner = owner;
94 WSTrustSecurityTokenServiceProxy proxy;
96 protected internal override TimeSpan DefaultCloseTimeout {
97 get { throw new NotImplementedException (); }
100 protected internal override TimeSpan DefaultOpenTimeout {
101 get { throw new NotImplementedException (); }
104 public override Message ProcessNegotiation (Message request)
106 if (request.Headers.Action == Constants.WstIssueAction)
107 return ProcessMessageType1 (request);
108 else
109 return ProcessMessageType3 (request);
112 class TlsServerSessionInfo
114 public TlsServerSessionInfo (string context, TlsServerSession tls)
116 ContextId = context;
117 Tls = tls;
120 public string ContextId;
121 public TlsServerSession Tls;
122 public MemoryStream Messages = new MemoryStream ();
125 Dictionary<string,SspiServerSession> sessions =
126 new Dictionary<string,SspiServerSession> ();
128 void AppendNegotiationMessageXml (XmlReader reader, TlsServerSessionInfo tlsInfo)
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);
141 Message ProcessMessageType1 (Message request)
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 ();
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));
152 Console.WriteLine (buffer.CreateMessage ());
154 SspiServerSession sspi = new SspiServerSession ();
155 // AppendNegotiationMessageXml (buffer.CreateMessage ().GetReaderAtBodyContents (), tlsInfo);
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.
162 byte [] raw = reader.Value.BinaryExchange.Value;
164 bool gss = "NTLMSSP" != Encoding.ASCII.GetString (raw, 0, 7);
166 if (gss)
167 sspi.ProcessSpnegoInitialContextTokenRequest (raw);
168 else
169 sspi.ProcessMessageType1 (raw);
171 WstRequestSecurityTokenResponse rstr =
172 new WstRequestSecurityTokenResponse (SecurityTokenSerializer);
173 rstr.Context = reader.Value.Context;
174 rstr.BinaryExchange = new WstBinaryExchange (Constants.WstBinaryExchangeValueGss);
176 if (gss)
177 rstr.BinaryExchange.Value = sspi.ProcessSpnegoInitialContextTokenResponse ();
178 else
179 rstr.BinaryExchange.Value = sspi.ProcessMessageType2 ();
181 Message reply = Message.CreateMessage (request.Version, Constants.WstIssueReplyAction, rstr);
182 reply.Headers.RelatesTo = request.Headers.MessageId;
184 // FIXME: use correct buffer size
185 buffer = reply.CreateBufferedCopy (0x10000);
186 // AppendNegotiationMessageXml (buffer.CreateMessage ().GetReaderAtBodyContents (), tlsInfo);
188 sessions [reader.Value.Context] = sspi;
190 return buffer.CreateMessage ();
193 Message ProcessMessageType3 (Message request)
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 ();
202 byte [] raw = reader.Value.BinaryExchange.Value;
204 bool gss = "NTLMSSP" != Encoding.ASCII.GetString (raw, 0, 7);
206 foreach (byte b in raw) Console.Write ("{0:X02} ", b); Console.WriteLine ();
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));
212 if (gss)
213 sspi.ProcessSpnegoProcessContextToken (raw);
214 else
215 sspi.ProcessMessageType3 (raw);
217 throw new NotImplementedException ();
219 AppendNegotiationMessageXml (buffer.CreateMessage ().GetReaderAtBodyContents (), tlsInfo);
220 //Console.WriteLine (System.Text.Encoding.UTF8.GetString (tlsInfo.Messages.ToArray ()));
222 tls.ProcessClientKeyExchange (reader.Value.BinaryExchange.Value);
224 byte [] serverFinished = tls.ProcessServerFinished ();
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 ();
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;
264 col.Responses.Add (rstr);
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);
272 sessions.Remove (reader.Value.Context);
274 return Message.CreateMessage (request.Version, Constants.WstIssueReplyAction, col);
278 protected override void OnAbort ()
280 throw new NotImplementedException ();
283 protected override void OnOpen (TimeSpan timeout)
285 if (State == CommunicationState.Opened)
286 throw new InvalidOperationException ("Already opened.");
288 EnsureProperties ();
290 proxy = new WSTrustSecurityTokenServiceProxy (
291 IssuerBinding, IssuerAddress);
294 protected override IAsyncResult OnBeginOpen (TimeSpan timeout, AsyncCallback callback, object state)
296 throw new NotImplementedException ();
299 protected override void OnEndOpen (IAsyncResult result)
301 throw new NotImplementedException ();
304 protected override void OnClose (TimeSpan timeout)
306 if (proxy != null)
307 proxy.Close ();
310 protected override IAsyncResult OnBeginClose (TimeSpan timeout, AsyncCallback callback, object state)
312 throw new NotImplementedException ();
315 protected override void OnEndClose (IAsyncResult result)
317 throw new NotImplementedException ();