Adding reference source for System.Net
[mono.git] / mcs / class / referencesource / System / net / System / Net / _NtlmClient.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="_NtlmClient.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Net {
8     using System.Collections;
9     using System.Security.Authentication.ExtendedProtection;
10     using System.Security.Permissions;
11     using System.Globalization;
12
13     internal class NtlmClient : ISessionAuthenticationModule {
14
15         internal const string AuthType = "NTLM";
16         internal static string Signature = AuthType.ToLower(CultureInfo.InvariantCulture);
17         internal static int SignatureSize = Signature.Length;
18
19         public Authorization Authenticate(string challenge, WebRequest webRequest, ICredentials credentials) {
20             GlobalLog.Print("NtlmClient::Authenticate() challenge:[" + ValidationHelper.ToString(challenge) + "] webRequest#" + ValidationHelper.HashString(webRequest) + " credentials#" + ValidationHelper.HashString(credentials) + " calling DoAuthenticate()");
21             return DoAuthenticate(challenge, webRequest, credentials, false);
22         }
23
24         private Authorization DoAuthenticate(string challenge, WebRequest webRequest, ICredentials credentials, bool preAuthenticate) {
25             GlobalLog.Print("NtlmClient::DoAuthenticate() challenge:[" + ValidationHelper.ToString(challenge) + "] webRequest#" + ValidationHelper.HashString(webRequest) + " credentials#" + ValidationHelper.HashString(credentials) + " preAuthenticate:" + preAuthenticate.ToString());
26
27             GlobalLog.Assert(credentials != null, "NtlmClient::DoAuthenticate()|credentials == null");
28             if (credentials == null) {
29                 return null;
30             }
31
32             HttpWebRequest httpWebRequest = webRequest as HttpWebRequest;
33
34             GlobalLog.Assert(httpWebRequest != null, "NtlmClient::DoAuthenticate()|httpWebRequest == null");
35             GlobalLog.Assert(httpWebRequest.ChallengedUri != null, "NtlmClient::DoAuthenticate()|httpWebRequest.ChallengedUri == null");
36
37             NTAuthentication authSession = null;
38             string incoming = null;
39
40             if (!preAuthenticate) {
41                 int index = AuthenticationManager.FindSubstringNotInQuotes(challenge, Signature);
42                 if (index < 0) {
43                     return null;
44                 }
45
46                 int blobBegin = index + SignatureSize;
47
48                 //
49                 // there may be multiple challenges. If the next character after the
50                 // package name is not a comma then it is challenge data
51                 //
52                 if (challenge.Length > blobBegin && challenge[blobBegin] != ',') {
53                     ++blobBegin;
54                 }
55                 else {
56                     index = -1;
57                 }
58
59                 if (index >= 0 && challenge.Length > blobBegin)
60                 {
61                     // Strip other modules information in case of multiple challenges
62                     // i.e do not take ", NTLM" as part of the following Negotiate blob
63                     // Negotiate TlRMTVNTUAACAAAADgAOADgAAAA1wo ... MAbwBmAHQALgBjAG8AbQAAAAAA,NTLM
64                     index = challenge.IndexOf(',', blobBegin);
65                     if (index != -1)
66                         incoming = challenge.Substring(blobBegin, index - blobBegin);
67                     else
68                         incoming = challenge.Substring(blobBegin);
69                 }
70
71
72                 authSession = httpWebRequest.CurrentAuthenticationState.GetSecurityContext(this);
73                 GlobalLog.Print("NtlmClient::DoAuthenticate() key:" + ValidationHelper.HashString(httpWebRequest.CurrentAuthenticationState) + " retrieved authSession:" + ValidationHelper.HashString(authSession));
74             }
75
76             if (authSession==null) {
77                 NetworkCredential NC = credentials.GetCredential(httpWebRequest.ChallengedUri, Signature);
78                 GlobalLog.Print("NtlmClient::DoAuthenticate() GetCredential() returns:" + ValidationHelper.ToString(NC));
79
80                 string username = string.Empty;
81                 if (NC == null || (!(NC is SystemNetworkCredential) && (username = NC.InternalGetUserName()).Length == 0))
82                 {
83                     return null;
84                 }
85
86                 ICredentialPolicy policy = AuthenticationManager.CredentialPolicy;
87                 if (policy != null && !policy.ShouldSendCredential(httpWebRequest.ChallengedUri, httpWebRequest, NC, this))
88                     return null;
89
90                 SpnToken spn = httpWebRequest.CurrentAuthenticationState.GetComputeSpn(httpWebRequest);
91                 GlobalLog.Print("NtlmClient::Authenticate() ChallengedSpn:" + ValidationHelper.ToString(spn));
92
93                 ChannelBinding binding = null;
94                 if (httpWebRequest.CurrentAuthenticationState.TransportContext != null)
95                 {
96                     binding = httpWebRequest.CurrentAuthenticationState.TransportContext.GetChannelBinding(ChannelBindingKind.Endpoint);
97                 }
98
99                 authSession =
100                     new NTAuthentication(
101                         AuthType,
102                         NC,
103                         spn,
104                         httpWebRequest,
105                         binding);
106
107                 GlobalLog.Print("NtlmClient::DoAuthenticate() setting SecurityContext for:" + ValidationHelper.HashString(httpWebRequest.CurrentAuthenticationState) + " to authSession:" + ValidationHelper.HashString(authSession));
108                 httpWebRequest.CurrentAuthenticationState.SetSecurityContext(authSession, this);
109             }
110
111             string clientResponse = authSession.GetOutgoingBlob(incoming);
112             if (clientResponse==null) {
113                 return null;
114             }
115
116             bool canShareConnection = httpWebRequest.UnsafeOrProxyAuthenticatedConnectionSharing;
117             if (canShareConnection) {
118                 httpWebRequest.LockConnection = true;
119             }
120
121             // this is the first leg of an NTLM handshake,
122             // set the NtlmKeepAlive override *STRICTLY* only in this case.
123             httpWebRequest.NtlmKeepAlive = incoming==null;
124
125             return AuthenticationManager.GetGroupAuthorization(this, AuthType + " " + clientResponse, authSession.IsCompleted, authSession, canShareConnection, false);
126         }
127
128         public bool CanPreAuthenticate {
129             get {
130                 return true;
131             }
132         }
133
134         public Authorization PreAuthenticate(WebRequest webRequest, ICredentials credentials) {
135             GlobalLog.Print("NtlmClient::PreAuthenticate() webRequest#" + ValidationHelper.HashString(webRequest) + " credentials#" + ValidationHelper.HashString(credentials) + " calling DoAuthenticate()");
136             return DoAuthenticate(null, webRequest, credentials, true);
137         }
138
139         public string AuthenticationType {
140             get {
141                 return AuthType;
142             }
143         }
144
145         //
146         // called when getting the final blob on the 200 OK from the server
147         //
148         public bool Update(string challenge, WebRequest webRequest) {
149             GlobalLog.Print("NtlmClient::Update(): " + challenge);
150
151             HttpWebRequest httpWebRequest = webRequest as HttpWebRequest;
152
153             GlobalLog.Assert(httpWebRequest != null, "NtlmClient::Update()|httpWebRequest == null");
154             GlobalLog.Assert(httpWebRequest.ChallengedUri != null, "NtlmClient::Update()|httpWebRequest.ChallengedUri == null");
155
156             //
157             // try to retrieve the state of the ongoing handshake
158             //
159             NTAuthentication authSession = httpWebRequest.CurrentAuthenticationState.GetSecurityContext(this);
160             GlobalLog.Print("NtlmClient::Update() key:" + ValidationHelper.HashString(httpWebRequest.CurrentAuthenticationState) + " retrieved authSession:" + ValidationHelper.HashString(authSession));
161
162             if (authSession==null) {
163                 GlobalLog.Print("NtlmClient::Update() null session returning true");
164                 return true;
165             }
166
167             GlobalLog.Print("NtlmClient::Update() authSession.IsCompleted:" + authSession.IsCompleted.ToString());
168
169             if (!authSession.IsCompleted && httpWebRequest.CurrentAuthenticationState.StatusCodeMatch==httpWebRequest.ResponseStatusCode) {
170                 GlobalLog.Print("NtlmClient::Update() still handshaking (based on status code) returning false");
171                 return false;
172             }
173
174             ClearSession(httpWebRequest);
175
176             // now possibly close the ConnectionGroup after authentication is done.
177             if (!httpWebRequest.UnsafeOrProxyAuthenticatedConnectionSharing) {
178                 GlobalLog.Print("NtlmClient::Update() releasing ConnectionGroup:" + httpWebRequest.GetConnectionGroupLine());
179                 httpWebRequest.ServicePoint.ReleaseConnectionGroup(httpWebRequest.GetConnectionGroupLine());
180             }
181
182             // Extract the CBT we used and cache it for future requests that want to do preauth
183             httpWebRequest.ServicePoint.SetCachedChannelBinding(httpWebRequest.ChallengedUri, authSession.ChannelBinding);
184
185             GlobalLog.Print("NtlmClient::Update() session removed and ConnectionGorup released returning true");
186             return true;
187         }
188
189         public void ClearSession(WebRequest webRequest) {
190             HttpWebRequest httpWebRequest = webRequest as HttpWebRequest;
191             GlobalLog.Assert(httpWebRequest != null, "NtlmClient::ClearSession()|httpWebRequest == null");
192             httpWebRequest.CurrentAuthenticationState.ClearSession();
193         }
194
195         public bool CanUseDefaultCredentials {
196             get {
197                 return true;
198             }
199         }
200
201     }; // class NtlmClient
202
203
204 } // namespace System.Net