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 ();