source: binary-improvements/MapRendering/Web/OpenID.cs@ 338

Last change on this file since 338 was 326, checked in by alloc, 6 years ago

More cleanup, allocation improvements

File size: 7.6 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Collections.Specialized;
4using System.IO;
5using System.Net;
6using System.Net.Security;
7using System.Reflection;
8using System.Security.Cryptography.X509Certificates;
9using System.Text;
10using System.Text.RegularExpressions;
11
12namespace AllocsFixes.NetConnections.Servers.Web {
13 public static class OpenID {
14 private const string STEAM_LOGIN = "https://steamcommunity.com/openid/login";
15
16 private static readonly Regex steamIdUrlMatcher =
17 new Regex (@"^https?:\/\/steamcommunity\.com\/openid\/id\/([0-9]{17,18})");
18
19 private static readonly X509Certificate2 caCert =
20 new X509Certificate2 (Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) +
21 "/steam-rootca.cer");
22
23 private static readonly X509Certificate2 caIntermediateCert =
24 new X509Certificate2 (Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) +
25 "/steam-intermediate.cer");
26
27 private static readonly bool verboseSsl = false;
28 public static bool debugOpenId;
29
30 static OpenID () {
31 for (int i = 0; i < Environment.GetCommandLineArgs ().Length; i++) {
32 if (Environment.GetCommandLineArgs () [i].EqualsCaseInsensitive ("-debugopenid")) {
33 debugOpenId = true;
34 }
35 }
36
37 ServicePointManager.ServerCertificateValidationCallback = (srvPoint, certificate, chain, errors) => {
38 if (errors == SslPolicyErrors.None) {
39 if (verboseSsl) {
40 Log.Out ("Steam certificate: No error (1)");
41 }
42
43 return true;
44 }
45
46 X509Chain privateChain = new X509Chain ();
47 privateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
48
49 privateChain.ChainPolicy.ExtraStore.Add (caCert);
50 privateChain.ChainPolicy.ExtraStore.Add (caIntermediateCert);
51
52 if (privateChain.Build (new X509Certificate2 (certificate))) {
53 // No errors, immediately return
54 privateChain.Reset ();
55 if (verboseSsl) {
56 Log.Out ("Steam certificate: No error (2)");
57 }
58
59 return true;
60 }
61
62 if (privateChain.ChainStatus.Length == 0) {
63 // No errors, immediately return
64 privateChain.Reset ();
65 if (verboseSsl) {
66 Log.Out ("Steam certificate: No error (3)");
67 }
68
69 return true;
70 }
71
72 // Iterate all chain elements
73 foreach (X509ChainElement chainEl in privateChain.ChainElements) {
74 if (verboseSsl) {
75 Log.Out ("Validating cert: " + chainEl.Certificate.Subject);
76 }
77
78 // Iterate all status flags of the current cert
79 foreach (X509ChainStatus chainStatus in chainEl.ChainElementStatus) {
80 if (verboseSsl) {
81 Log.Out (" Status: " + chainStatus.Status);
82 }
83
84 if (chainStatus.Status == X509ChainStatusFlags.NoError) {
85 // This status is not an error, skip
86 continue;
87 }
88
89 if (chainStatus.Status == X509ChainStatusFlags.UntrustedRoot && chainEl.Certificate.Equals (caCert)) {
90 // This status is about the cert being an untrusted root certificate but the certificate is one of those we added, ignore
91 continue;
92 }
93
94 // This status is an error, print information
95 Log.Warning ("Steam certificate error: " + chainEl.Certificate.Subject + " ### Error: " +
96 chainStatus.Status);
97 privateChain.Reset ();
98 return false;
99 }
100 }
101
102 foreach (X509ChainStatus chainStatus in privateChain.ChainStatus) {
103 if (chainStatus.Status != X509ChainStatusFlags.NoError &&
104 chainStatus.Status != X509ChainStatusFlags.UntrustedRoot) {
105 Log.Warning ("Steam certificate error: " + chainStatus.Status);
106 privateChain.Reset ();
107 return false;
108 }
109 }
110
111 // We didn't find any errors, chain is valid
112 privateChain.Reset ();
113 if (verboseSsl) {
114 Log.Out ("Steam certificate: No error (4)");
115 }
116
117 return true;
118 };
119 }
120
121 public static string GetOpenIdLoginUrl (string _returnHost, string _returnUrl) {
122 Dictionary<string, string> queryParams = new Dictionary<string, string> ();
123
124 queryParams.Add ("openid.ns", "http://specs.openid.net/auth/2.0");
125 queryParams.Add ("openid.mode", "checkid_setup");
126 queryParams.Add ("openid.return_to", _returnUrl);
127 queryParams.Add ("openid.realm", _returnHost);
128 queryParams.Add ("openid.identity", "http://specs.openid.net/auth/2.0/identifier_select");
129 queryParams.Add ("openid.claimed_id", "http://specs.openid.net/auth/2.0/identifier_select");
130
131 return STEAM_LOGIN + '?' + buildUrlParams (queryParams);
132 }
133
134 public static ulong Validate (HttpListenerRequest _req) {
135 string mode = getValue (_req, "openid.mode");
136 if (mode == "cancel") {
137 Log.Warning ("Steam OpenID login canceled");
138 return 0;
139 }
140
141 if (mode == "error") {
142 Log.Warning ("Steam OpenID login error: " + getValue (_req, "openid.error"));
143 if (debugOpenId) {
144 PrintOpenIdResponse (_req);
145 }
146
147 return 0;
148 }
149
150 string steamIdString = getValue (_req, "openid.claimed_id");
151 ulong steamId;
152 Match steamIdMatch = steamIdUrlMatcher.Match (steamIdString);
153 if (steamIdMatch.Success) {
154 steamId = ulong.Parse (steamIdMatch.Groups [1].Value);
155 } else {
156 Log.Warning ("Steam OpenID login result did not give a valid SteamID");
157 if (debugOpenId) {
158 PrintOpenIdResponse (_req);
159 }
160
161 return 0;
162 }
163
164 Dictionary<string, string> queryParams = new Dictionary<string, string> ();
165
166 queryParams.Add ("openid.ns", "http://specs.openid.net/auth/2.0");
167
168 queryParams.Add ("openid.assoc_handle", getValue (_req, "openid.assoc_handle"));
169 queryParams.Add ("openid.signed", getValue (_req, "openid.signed"));
170 queryParams.Add ("openid.sig", getValue (_req, "openid.sig"));
171 queryParams.Add ("openid.identity", "http://specs.openid.net/auth/2.0/identifier_select");
172 queryParams.Add ("openid.claimed_id", "http://specs.openid.net/auth/2.0/identifier_select");
173
174 string[] signeds = getValue (_req, "openid.signed").Split (',');
175 foreach (string s in signeds) {
176 queryParams ["openid." + s] = getValue (_req, "openid." + s);
177 }
178
179 queryParams.Add ("openid.mode", "check_authentication");
180
181 byte[] postData = Encoding.ASCII.GetBytes (buildUrlParams (queryParams));
182 HttpWebRequest request = (HttpWebRequest) WebRequest.Create (STEAM_LOGIN);
183 request.Method = "POST";
184 request.ContentType = "application/x-www-form-urlencoded";
185 request.ContentLength = postData.Length;
186 request.Headers.Add (HttpRequestHeader.AcceptLanguage, "en");
187 using (Stream st = request.GetRequestStream ()) {
188 st.Write (postData, 0, postData.Length);
189 }
190
191 HttpWebResponse response = (HttpWebResponse) request.GetResponse ();
192 string responseString;
193 using (Stream st = response.GetResponseStream ()) {
194 using (StreamReader str = new StreamReader (st)) {
195 responseString = str.ReadToEnd ();
196 }
197 }
198
199 if (responseString.ContainsCaseInsensitive ("is_valid:true")) {
200 return steamId;
201 }
202
203 Log.Warning ("Steam OpenID login failed: {0}", responseString);
204 return 0;
205 }
206
207 private static string buildUrlParams (Dictionary<string, string> _queryParams) {
208 string[] paramsArr = new string[_queryParams.Count];
209 int i = 0;
210 foreach (KeyValuePair<string, string> kvp in _queryParams) {
211 paramsArr [i++] = kvp.Key + "=" + Uri.EscapeDataString (kvp.Value);
212 }
213
214 return string.Join ("&", paramsArr);
215 }
216
217 private static string getValue (HttpListenerRequest _req, string _name) {
218 NameValueCollection nvc = _req.QueryString;
219 if (nvc [_name] == null) {
220 throw new MissingMemberException ("OpenID parameter \"" + _name + "\" missing");
221 }
222
223 return nvc [_name];
224 }
225
226 private static void PrintOpenIdResponse (HttpListenerRequest _req) {
227 NameValueCollection nvc = _req.QueryString;
228 for (int i = 0; i < nvc.Count; i++) {
229 Log.Out (" " + nvc.GetKey (i) + " = " + nvc [i]);
230 }
231 }
232 }
233}
Note: See TracBrowser for help on using the repository browser.