Merge branch 'master' of http://github.com/mono/mono
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel / ClientCredentialsSecurityTokenManager.cs
1 //
2 // ClientCredentialsSecurityTokenManager.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2006 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.Net.Security;
30 using System.IdentityModel.Selectors;
31 using System.IdentityModel.Tokens;
32 using System.Security.Cryptography.X509Certificates;
33 using System.Security.Principal;
34 using System.ServiceModel.Channels;
35 using System.ServiceModel.Description;
36 using System.ServiceModel.Security;
37 using System.ServiceModel.Security.Tokens;
38
39 using ReqType = System.ServiceModel.Security.Tokens.ServiceModelSecurityTokenRequirement;
40
41 namespace System.ServiceModel
42 {
43         [MonoTODO]
44         public class ClientCredentialsSecurityTokenManager : SecurityTokenManager
45         {
46                 ClientCredentials credentials;
47
48                 public ClientCredentialsSecurityTokenManager (ClientCredentials credentials)
49                 {
50                         if (credentials == null)
51                                 throw new ArgumentNullException ("credentials");
52                         this.credentials = credentials;
53                 }
54
55                 public ClientCredentials ClientCredentials {
56                         get { return credentials; }
57                 }
58
59                 [MonoTODO]
60                 public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator (
61                         SecurityTokenRequirement requirement,
62                         out SecurityTokenResolver outOfBandTokenResolver)
63                 {
64                         outOfBandTokenResolver = null;
65                         if (requirement == null)
66                                 throw new ArgumentNullException ("requirement");
67                         if (requirement.TokenType == SecurityTokenTypes.UserName) {
68                                 // unsupported
69                         }
70                         else if (requirement.TokenType == SecurityTokenTypes.Rsa)
71                                 return new RsaSecurityTokenAuthenticator ();
72                         else if (requirement.TokenType == SecurityTokenTypes.X509Certificate)
73                                 return CreateX509Authenticator (requirement);
74                         else if (requirement.TokenType == ServiceModelSecurityTokenTypes.Spnego)
75                                 return new SspiClientSecurityTokenAuthenticator (this, requirement);
76                         else
77                                 throw new NotImplementedException ("Security token type " + requirement.TokenType);
78
79                         throw new NotSupportedException (String.Format ("Security token requirement '{0}' is not supported to create SecurityTokenAuthenticator.", requirement));
80                 }
81
82
83                 X509SecurityTokenAuthenticator CreateX509Authenticator (SecurityTokenRequirement requirement)
84                 {
85                         X509CertificateRecipientClientCredential c = ClientCredentials.ServiceCertificate;
86                         switch (c.Authentication.CertificateValidationMode) {
87                         case X509CertificateValidationMode.Custom:
88                                 if (c.Authentication.CustomCertificateValidator == null)
89                                         throw new InvalidOperationException ("For Custom certificate validation mode, CustomCertificateValidator is required to create a token authenticator for X509 certificate.");
90                                 return new X509SecurityTokenAuthenticator (c.Authentication.CustomCertificateValidator);
91                         case X509CertificateValidationMode.None:
92                                 return new X509SecurityTokenAuthenticator (X509CertificateValidator.None);
93                         case X509CertificateValidationMode.PeerOrChainTrust:
94                                 return new X509SecurityTokenAuthenticator (X509CertificateValidator.PeerOrChainTrust);
95                         case X509CertificateValidationMode.ChainTrust:
96                                 return new X509SecurityTokenAuthenticator (X509CertificateValidator.ChainTrust);
97                         default:
98                                 return new X509SecurityTokenAuthenticator (X509CertificateValidator.PeerTrust);
99                         }
100                 }
101
102                 #region CreateSecurityTokenProvider()
103
104                 [MonoTODO]
105                 public override SecurityTokenProvider CreateSecurityTokenProvider (SecurityTokenRequirement requirement)
106                 {
107                         if (IsIssuedSecurityTokenRequirement (requirement))
108                                 return CreateIssuedTokenProvider (requirement);
109
110                         bool isInitiator;
111
112                         // huh, they are not constants but properties.
113                         if (requirement.TokenType == SecurityTokenTypes.X509Certificate)
114                                 return CreateX509SecurityTokenProvider (requirement);
115                         else if (requirement.TokenType == ServiceModelSecurityTokenTypes.SecureConversation)
116                                 return CreateSecureConversationProvider (requirement);
117                         else if (requirement.TokenType == ServiceModelSecurityTokenTypes.AnonymousSslnego) {
118                                 if (requirement.TryGetProperty<bool> (ReqType.IsInitiatorProperty, out isInitiator) && isInitiator)
119                                         return CreateSslnegoProvider (requirement);
120                         } else if (requirement.TokenType == ServiceModelSecurityTokenTypes.MutualSslnego) {
121                                 if (requirement.TryGetProperty<bool> (ReqType.IsInitiatorProperty, out isInitiator) && isInitiator)
122                                         return CreateSslnegoProvider (requirement);
123                         } else if (requirement.TokenType == ServiceModelSecurityTokenTypes.SecurityContext) {
124                                 // FIXME: implement
125                         } else if (requirement.TokenType == ServiceModelSecurityTokenTypes.Spnego) {
126                                 return CreateSpnegoProvider (requirement);
127                         } else if (requirement.TokenType == ServiceModelSecurityTokenTypes.SspiCredential) {
128                                 // FIXME: implement
129                         } else if (requirement.TokenType == SecurityTokenTypes.Rsa) {
130                                 // FIXME: implement
131                         } else if (requirement.TokenType == SecurityTokenTypes.Saml) {
132                                 // FIXME: implement
133                         } else if (requirement.TokenType == SecurityTokenTypes.UserName)
134                                 return CreateUserNameProvider (requirement);
135                         else if (requirement.TokenType == SecurityTokenTypes.Kerberos) {
136                                 return CreateKerberosProvider (requirement);
137                         }
138                         throw new NotSupportedException (String.Format ("Token type '{0}' is not supported", requirement.TokenType));
139                 }
140
141                 UserNameSecurityTokenProvider CreateUserNameProvider (
142 SecurityTokenRequirement requirement)
143                 {
144                         UserNamePasswordClientCredential c =
145                                 credentials.UserName;
146                         if (c.UserName == null)
147                                 throw new InvalidOperationException ("User name is not specified in ClientCredentials.");
148                         UserNameSecurityTokenProvider p =
149                                 new UserNameSecurityTokenProvider (c.UserName, c.Password);
150                         return p;
151                 }
152
153                 KerberosSecurityTokenProvider CreateKerberosProvider (SecurityTokenRequirement requirement)
154                 {
155                         // FIXME: how to get SPN?
156                         return new KerberosSecurityTokenProvider (
157                                 "", credentials.Windows.AllowedImpersonationLevel, credentials.Windows.ClientCredential);
158                 }
159
160                 X509SecurityTokenProvider CreateX509SecurityTokenProvider (SecurityTokenRequirement requirement)
161                 {
162                         // - When the request is as an initiator, then
163                         //   - if the purpose is key exchange, then
164                         //     the initiator wants the service certificate
165                         //     to encrypt the message with its public key.
166                         //   - otherwise, the initiator wants the client
167                         //     certificate to sign the message with the
168                         //     private key.
169                         // - otherwise
170                         //   - if the purpose is key exchange, then
171                         //     the recipient wants the client certificate
172                         //     to encrypt the message with its public key.
173                         //   - otherwise, the recipient wants the service
174                         //     certificate to sign the message with the
175                         //     private key.
176                         bool isInitiator;
177                         if (!requirement.TryGetProperty<bool> (ReqType.IsInitiatorProperty, out isInitiator))
178                                 isInitiator = false;
179                         X509Certificate2 cert;
180                         bool isClient;
181                         if (isInitiator)
182                                 isClient = requirement.KeyUsage == SecurityKeyUsage.Signature;
183                         else {
184                                 if (!requirement.Properties.ContainsKey (SecurityTokenRequirement.KeyUsageProperty))
185                                         throw new NotSupportedException (String.Format ("Cannot create a security token provider from this requirement '{0}'", requirement));
186                                 isClient = requirement.KeyUsage == SecurityKeyUsage.Exchange;
187                         }
188                         if (isClient)
189                                 cert = credentials.ClientCertificate.Certificate;
190                         else
191                                 cert = GetServiceCertificate (requirement);
192
193                         if (cert == null) {
194                                 if (isClient)
195                                         throw new InvalidOperationException ("Client certificate is not provided in ClientCredentials.");
196                                 else
197                                         throw new InvalidOperationException ("Service certificate is not provided.");
198                         }
199                         X509SecurityTokenProvider p =
200                                 new X509SecurityTokenProvider (cert);
201                         return p;
202                 }
203
204                 X509Certificate2 GetServiceCertificate (SecurityTokenRequirement requirement)
205                 {
206                         // try X509CertificateEndpointIdentity,
207                         // ServiceCertificate.ScopedCertificate and
208                         // ServiceCertificate.DefaultCertificate.
209
210                         X509Certificate2 cert = null;
211                         EndpointAddress address = null;
212                         requirement.TryGetProperty (ReqType.TargetAddressProperty, out address);
213
214                         if (address != null) {
215                                 X509CertificateEndpointIdentity ident = address.Identity as X509CertificateEndpointIdentity;
216                                 if (ident != null && ident.Certificates.Count > 0)
217                                         cert = ident.Certificates [0];
218                                 if (cert == null)
219                                         credentials.ServiceCertificate.ScopedCertificates.TryGetValue (address.Uri, out cert);
220                         }
221                         if (cert == null)
222                                 cert = credentials.ServiceCertificate.DefaultCertificate;
223                         return cert;
224                 }
225
226                 void InitializeProviderCommunicationObject (ProviderCommunicationObject p, SecurityTokenRequirement r)
227                 {
228                         p.TargetAddress = r.GetProperty<EndpointAddress> (ReqType.TargetAddressProperty);
229
230                         // FIXME: use it somewhere, probably to build 
231                         // IssuerBinding. However, there is also IssuerBinding 
232                         // property. SecureConversationSecurityBindingElement
233                         // as well.
234                         SecurityBindingElement sbe =
235                                 r.GetProperty<SecurityBindingElement> (ReqType.SecurityBindingElementProperty);
236
237                         // I doubt the binding is acquired this way ...
238                         Binding binding;
239                         if (!r.TryGetProperty<Binding> (ReqType.IssuerBindingProperty, out binding))
240                                 binding = new CustomBinding (
241                                         new TextMessageEncodingBindingElement (),
242                                         new HttpTransportBindingElement ());
243                         p.IssuerBinding = binding;
244
245                         // not sure if it is used only for this purpose though ...
246                         BindingContext ctx = r.GetProperty<BindingContext> (ReqType.IssuerBindingContextProperty);
247                         foreach (IEndpointBehavior b in ctx.BindingParameters.FindAll<IEndpointBehavior> ())
248                                 p.IssuerChannelBehaviors.Add (b);
249
250                         SecurityTokenVersion ver =
251                                 r.GetProperty<SecurityTokenVersion> (ReqType.MessageSecurityVersionProperty);
252                         p.SecurityTokenSerializer =
253                                 CreateSecurityTokenSerializer (ver);
254
255                         // seems like they are optional here ... (but possibly
256                         // used later)
257                         EndpointAddress address;
258                         if (!r.TryGetProperty<EndpointAddress> (ReqType.IssuerAddressProperty, out address))
259                                 address = p.TargetAddress;
260                         p.IssuerAddress = address;
261
262                         // It is somehow not checked as mandatory ...
263                         SecurityAlgorithmSuite suite = null;
264                         r.TryGetProperty<SecurityAlgorithmSuite> (ReqType.SecurityAlgorithmSuiteProperty, out suite);
265                         p.SecurityAlgorithmSuite = suite;
266                 }
267
268                 // FIXME: it is far from done.
269                 SecurityTokenProvider CreateSecureConversationProvider (SecurityTokenRequirement r)
270                 {
271                         IssuedSecurityTokenProvider p =
272                                 new IssuedSecurityTokenProvider ();
273                         InitializeProviderCommunicationObject (p.Communication, r);
274
275                         // FIXME: use it somewhere.
276                         int keySize = r.KeySize;
277
278                         return p;
279                 }
280
281                 SecurityTokenProvider CreateSslnegoProvider (SecurityTokenRequirement r)
282                 {
283                         SslSecurityTokenProvider p = new SslSecurityTokenProvider (this, r.TokenType == ServiceModelSecurityTokenTypes.MutualSslnego);
284                         InitializeProviderCommunicationObject (p.Communication, r);
285
286                         return p;
287                 }
288
289                 SecurityTokenProvider CreateSpnegoProvider (SecurityTokenRequirement r)
290                 {
291                         SpnegoSecurityTokenProvider p = new SpnegoSecurityTokenProvider (this, r);
292                         InitializeProviderCommunicationObject (p.Communication, r);
293
294                         return p;
295                 }
296
297                 IssuedSecurityTokenProvider CreateIssuedTokenProvider (SecurityTokenRequirement requirement)
298                 {
299                         IssuedSecurityTokenProvider p =
300                                 new IssuedSecurityTokenProvider ();
301                         // FIXME: fill properties
302                         EndpointAddress address;
303                         if (requirement.TryGetProperty<EndpointAddress> (ReqType.IssuerAddressProperty, out address))
304                                 p.IssuerAddress = address;
305                         if (requirement.TryGetProperty<EndpointAddress> (ReqType.TargetAddressProperty, out address))
306                                 p.TargetAddress = address;
307                         Binding binding;
308                         if (requirement.TryGetProperty<Binding> (ReqType.IssuerBindingProperty, out binding))
309                                 p.IssuerBinding = binding;
310                         MessageSecurityVersion ver;
311                         if (requirement.TryGetProperty<MessageSecurityVersion> (ReqType.MessageSecurityVersionProperty, out ver))
312                                 p.SecurityTokenSerializer = CreateSecurityTokenSerializer (ver.SecurityVersion);
313                         SecurityAlgorithmSuite suite;
314                         if (requirement.TryGetProperty<SecurityAlgorithmSuite> (ReqType.SecurityAlgorithmSuiteProperty, out suite))
315                                 p.SecurityAlgorithmSuite = suite;
316                         return p;
317                 }
318
319                 #endregion
320
321                 public override SecurityTokenSerializer CreateSecurityTokenSerializer (SecurityTokenVersion version)
322                 {
323                         bool bsp = version.GetSecuritySpecifications ().Contains (Constants.WSBasicSecurityProfileCore1);
324                         SecurityVersion ver =
325                                 version.GetSecuritySpecifications ().Contains (Constants.Wss11Namespace) ?
326                                 SecurityVersion.WSSecurity11 :
327                                 SecurityVersion.WSSecurity10;
328                         return new WSSecurityTokenSerializer (ver, bsp);
329                 }
330
331                 protected SecurityTokenSerializer CreateSecurityTokenSerializer (SecurityVersion version)
332                 {
333                         return new WSSecurityTokenSerializer (version);
334                 }
335
336                 protected internal bool IsIssuedSecurityTokenRequirement (
337                         SecurityTokenRequirement requirement)
338                 {
339                         SecurityTokenParameters ret;
340                         if (!requirement.TryGetProperty<SecurityTokenParameters> (ServiceModelSecurityTokenRequirement.IssuedSecurityTokenParametersProperty, out ret))
341                                 return false;
342                         return ret is IssuedSecurityTokenParameters;
343                 }
344         }
345 }