2 // SpnegoSecurityTokenAuthenticator.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2007 Novell, Inc. http://www.novell.com
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
29 using System.Collections.Generic;
30 using System.Collections.ObjectModel;
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;
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 {
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)
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);
109 return ProcessMessageType3 (request);
112 class TlsServerSessionInfo
114 public TlsServerSessionInfo (string context, TlsServerSession 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));
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);
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);
167 sspi.ProcessSpnegoInitialContextTokenRequest (raw);
169 sspi.ProcessMessageType1 (raw);
171 WstRequestSecurityTokenResponse rstr =
172 new WstRequestSecurityTokenResponse (SecurityTokenSerializer);
173 rstr.Context = reader.Value.Context;
174 rstr.BinaryExchange = new WstBinaryExchange (Constants.WstBinaryExchangeValueGss);
177 rstr.BinaryExchange.Value = sspi.ProcessSpnegoInitialContextTokenResponse ();
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);
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));
213 sspi.ProcessSpnegoProcessContextToken (raw);
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.)
245 "uuid-" + Guid.NewGuid (),
248 // FIXME: use LocalServiceSecuritySettings.NegotiationTimeout
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 ();
258 // FIXME: use LocalServiceSecuritySettings.NegotiationTimeout
259 lt.Expires = from.AddHours (8);
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.");
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)
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 ();