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

Last change on this file since 317 was 317, checked in by alloc, 2 years ago

Added OpenID debugging to Web

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