Merge pull request #4246 from Unity-Technologies/mcs-generic-constraint-enumerator
[mono.git] / mcs / tools / security / crlupdate.cs
1 //
2 // crlupdate.cs: CRL downloader / updater
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // Copyright (C) 2011 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
29 using System;
30 using System.Net;
31 using System.Reflection;
32
33 using Mono.Security.X509;
34 using Mono.Security.X509.Extensions;
35
36 [assembly: AssemblyTitle ("Mono CRL Updater")]
37 [assembly: AssemblyDescription ("Download and update X.509 certificate revocation lists from your stores.")]
38
39 namespace Mono.Tools {
40
41         class CrlUpdater {
42
43                 static private void Header ()
44                 {
45                         Console.WriteLine (new AssemblyInfo ().ToString ());
46                 }
47
48                 static private void Help ()
49                 {
50                         Console.WriteLine ("Usage: crlupdate [-m] [-v] [-f] [-?]");
51                         Console.WriteLine ();
52                         Console.WriteLine ("\t-m\tuse the machine certificate store (default to user)");
53                         Console.WriteLine ("\t-v\tverbose mode (display status for every steps)");
54                         Console.WriteLine ("\t-f\tforce download (and replace existing CRL)");
55                         Console.WriteLine ("\t-?\tDisplay this help message");
56                         Console.WriteLine ();
57                 }
58
59                 static X509Certificate FindCrlIssuer (string name, byte[] aki, X509CertificateCollection col)
60                 {
61                         foreach (X509Certificate cert in col) {
62                                 if (name != cert.SubjectName)
63                                         continue;
64                                 if ((aki == null) || Compare (aki, GetSubjectKeyIdentifier (cert.Extensions ["2.5.29.14"])))
65                                         return cert;
66                         }
67                         return null;
68                 }
69
70                 static X509Certificate FindCrlIssuer (X509Crl crl)
71                 {
72                         string name = crl.IssuerName;
73                         byte [] aki = GetAuthorityKeyIdentifier (crl.Extensions ["2.5.29.35"]);
74                         X509Certificate cert = FindCrlIssuer (name, aki, X509StoreManager.IntermediateCACertificates);
75                         if (cert != null)
76                                 return cert;
77                         return FindCrlIssuer (name, aki, X509StoreManager.TrustedRootCertificates);
78                 }
79
80                 static X509Chain chain = new X509Chain ();
81
82                 static bool VerifyCrl (X509Crl crl)
83                 {
84                         X509Certificate issuer = FindCrlIssuer (crl);
85                         if (issuer == null)
86                                 return false;
87
88                         if (!crl.VerifySignature (issuer))
89                                 return false;
90
91                         chain.Reset ();
92                         return chain.Build (issuer);
93                 }
94
95                 static void Download (string url, X509Store store)
96                 {
97                         if (verbose)
98                                 Console.WriteLine ("Downloading: {0}", url);
99
100                         WebClient wc = new WebClient ();
101                         string error = "download";
102                         try {
103                                 byte [] data = wc.DownloadData (url);
104                                 error = "decode";
105                                 X509Crl crl = new X509Crl (data);
106                                 error = "import";
107                                 // warn if CRL is not current - but still allow it to be imported
108                                 if (!crl.IsCurrent && verbose)
109                                         Console.WriteLine ("WARNING: CRL is not current: {0}", url);
110
111                                 // only import the CRL if its signature is valid and coming from a trusted root
112                                 if (VerifyCrl (crl))
113                                         store.Import (crl);
114                                 else
115                                         Console.WriteLine ("ERROR: could not validate CRL: {0}", url);
116                         }
117                         catch (Exception e) {
118                                 Console.WriteLine ("ERROR: could not {0}: {1}", error, url);
119                                 if (verbose) {
120                                         Console.WriteLine (e);
121                                         Console.WriteLine ();
122                                 }
123                         }
124                 }
125
126                 static byte [] GetAuthorityKeyIdentifier (X509Extension ext)
127                 {
128                         if (ext == null)
129                                 return null;
130
131                         AuthorityKeyIdentifierExtension aki = new AuthorityKeyIdentifierExtension (ext);
132                         return aki.Identifier;
133                 }
134
135                 static byte [] GetSubjectKeyIdentifier (X509Extension ext)
136                 {
137                         if (ext == null)
138                                 return null;
139
140                         SubjectKeyIdentifierExtension ski = new SubjectKeyIdentifierExtension (ext);
141                         return ski.Identifier;
142                 }
143
144                 static bool Compare (byte [] a, byte [] b)
145                 {
146                         if (a == null)
147                                 return (b == null);
148                         else if (b == null)
149                                 return false;
150
151                         if (a.Length != b.Length)
152                                 return false;
153
154                         for (int i = 0; i < a.Length; i++) {
155                                 if (a [i] != b [i])
156                                         return false;
157                         }
158                         return true;
159                 }
160
161                 static X509Crl FindCrl (X509Certificate cert, X509Store store)
162                 {
163                         string name = cert.SubjectName;
164                         byte [] ski = GetSubjectKeyIdentifier (cert.Extensions ["2.5.29.14"]);
165                         foreach (X509Crl crl in store.Crls) {
166                                 if (crl.IssuerName != name)
167                                         continue;
168                                 if ((ski == null) || Compare (ski, GetAuthorityKeyIdentifier (crl.Extensions ["2.5.29.35"])))
169                                         return crl;
170                         }
171                         return null;
172                 }
173
174                 static void UpdateStore (X509Store store)
175                 {
176                         // for each certificate
177                         foreach (X509Certificate cert in store.Certificates) {
178
179                                 // do we already have a matching CRL ? (or are we forced to download?)
180                                 X509Crl crl = force ? null : FindCrl (cert, store);
181                                 // without a CRL (or with a CRL in need of updating)
182                                 if ((crl == null) || !crl.IsCurrent) {
183                                         X509Extension ext = cert.Extensions ["2.5.29.31"];
184                                         if (ext == null) {
185                                                 if (verbose)
186                                                         Console.WriteLine ("WARNING: No cRL distribution point found for '{0}'", cert.SubjectName);
187                                                 continue;
188                                         }
189
190                                         CRLDistributionPointsExtension crlDP = new CRLDistributionPointsExtension (ext);
191                                         foreach (var dp in crlDP.DistributionPoints) {
192                                                 string name = dp.Name.Trim ();
193                                                 if (name.StartsWith ("URL="))
194                                                         Download (name.Substring (4), store);
195                                                 else if (verbose)
196                                                         Console.WriteLine ("WARNING: Unsupported distribution point: '{0}'", name);
197                                         }
198                                 }
199                         }
200                 }
201
202                 static bool verbose = false;
203                 static bool force = false;
204
205                 static int Main (string [] args)
206                 {
207                         bool machine = false;
208
209                         for (int i = 0; i < args.Length; i++) {
210                                 switch (args [i]) {
211                                 case "-m":
212                                 case "--m":
213                                         machine = true;
214                                         break;
215                                 case "-v":
216                                 case "--v":
217                                         verbose = true;
218                                         break;
219                                 case "-f":
220                                 case "--f":
221                                         force = true;
222                                         break;
223                                 case "-help":
224                                 case "--help":
225                                 case "-?":
226                                 case "--?":
227                                         Help ();
228                                         return 0;
229                                 }
230                         }
231
232                         try {
233                                 X509Stores stores = ((machine) ? X509StoreManager.LocalMachine : X509StoreManager.CurrentUser);
234                                 // for all store (expect Untrusted)
235                                 UpdateStore (stores.TrustedRoot);
236                                 UpdateStore (stores.IntermediateCA);
237                                 UpdateStore (stores.Personal);
238                                 UpdateStore (stores.OtherPeople);
239                                 return 0;
240                         }
241                         catch (Exception e) {
242                                 Console.WriteLine ("ERROR: Unexpected exception: {0}", e);
243                                 return 1;
244                         }
245                 }
246         }
247 }