Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

248 lines
6.9 KiB
C#

//
// crlupdate.cs: CRL downloader / updater
//
// Author:
// Sebastien Pouliot <sebastien@ximian.com>
//
// Copyright (C) 2011 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Net;
using System.Reflection;
using Mono.Security.X509;
using Mono.Security.X509.Extensions;
[assembly: AssemblyTitle ("Mono CRL Updater")]
[assembly: AssemblyDescription ("Download and update X.509 certificate revocation lists from your stores.")]
namespace Mono.Tools {
class CrlUpdater {
static private void Header ()
{
Console.WriteLine (new AssemblyInfo ().ToString ());
}
static private void Help ()
{
Console.WriteLine ("Usage: crlupdate [-m] [-v] [-f] [-?]");
Console.WriteLine ();
Console.WriteLine ("\t-m\tuse the machine certificate store (default to user)");
Console.WriteLine ("\t-v\tverbose mode (display status for every steps)");
Console.WriteLine ("\t-f\tforce download (and replace existing CRL)");
Console.WriteLine ("\t-?\tDisplay this help message");
Console.WriteLine ();
}
static X509Certificate FindCrlIssuer (string name, byte[] aki, X509CertificateCollection col)
{
foreach (X509Certificate cert in col) {
if (name != cert.SubjectName)
continue;
if ((aki == null) || Compare (aki, GetSubjectKeyIdentifier (cert.Extensions ["2.5.29.14"])))
return cert;
}
return null;
}
static X509Certificate FindCrlIssuer (X509Crl crl)
{
string name = crl.IssuerName;
byte [] aki = GetAuthorityKeyIdentifier (crl.Extensions ["2.5.29.35"]);
X509Certificate cert = FindCrlIssuer (name, aki, X509StoreManager.IntermediateCACertificates);
if (cert != null)
return cert;
return FindCrlIssuer (name, aki, X509StoreManager.TrustedRootCertificates);
}
static X509Chain chain = new X509Chain ();
static bool VerifyCrl (X509Crl crl)
{
X509Certificate issuer = FindCrlIssuer (crl);
if (issuer == null)
return false;
if (!crl.VerifySignature (issuer))
return false;
chain.Reset ();
return chain.Build (issuer);
}
static void Download (string url, X509Store store)
{
if (verbose)
Console.WriteLine ("Downloading: {0}", url);
WebClient wc = new WebClient ();
string error = "download";
try {
byte [] data = wc.DownloadData (url);
error = "decode";
X509Crl crl = new X509Crl (data);
error = "import";
// warn if CRL is not current - but still allow it to be imported
if (!crl.IsCurrent && verbose)
Console.WriteLine ("WARNING: CRL is not current: {0}", url);
// only import the CRL if its signature is valid and coming from a trusted root
if (VerifyCrl (crl))
store.Import (crl);
else
Console.WriteLine ("ERROR: could not validate CRL: {0}", url);
}
catch (Exception e) {
Console.WriteLine ("ERROR: could not {0}: {1}", error, url);
if (verbose) {
Console.WriteLine (e);
Console.WriteLine ();
}
}
}
static byte [] GetAuthorityKeyIdentifier (X509Extension ext)
{
if (ext == null)
return null;
AuthorityKeyIdentifierExtension aki = new AuthorityKeyIdentifierExtension (ext);
return aki.Identifier;
}
static byte [] GetSubjectKeyIdentifier (X509Extension ext)
{
if (ext == null)
return null;
SubjectKeyIdentifierExtension ski = new SubjectKeyIdentifierExtension (ext);
return ski.Identifier;
}
static bool Compare (byte [] a, byte [] b)
{
if (a == null)
return (b == null);
else if (b == null)
return false;
if (a.Length != b.Length)
return false;
for (int i = 0; i < a.Length; i++) {
if (a [i] != b [i])
return false;
}
return true;
}
static X509Crl FindCrl (X509Certificate cert, X509Store store)
{
string name = cert.SubjectName;
byte [] ski = GetSubjectKeyIdentifier (cert.Extensions ["2.5.29.14"]);
foreach (X509Crl crl in store.Crls) {
if (crl.IssuerName != name)
continue;
if ((ski == null) || Compare (ski, GetAuthorityKeyIdentifier (crl.Extensions ["2.5.29.35"])))
return crl;
}
return null;
}
static void UpdateStore (X509Store store)
{
// for each certificate
foreach (X509Certificate cert in store.Certificates) {
// do we already have a matching CRL ? (or are we forced to download?)
X509Crl crl = force ? null : FindCrl (cert, store);
// without a CRL (or with a CRL in need of updating)
if ((crl == null) || !crl.IsCurrent) {
X509Extension ext = cert.Extensions ["2.5.29.31"];
if (ext == null) {
if (verbose)
Console.WriteLine ("WARNING: No cRL distribution point found for '{0}'", cert.SubjectName);
continue;
}
CRLDistributionPointsExtension crlDP = new CRLDistributionPointsExtension (ext);
foreach (var dp in crlDP.DistributionPoints) {
string name = dp.Name.Trim ();
if (name.StartsWith ("URL="))
Download (name.Substring (4), store);
else if (verbose)
Console.WriteLine ("WARNING: Unsupported distribution point: '{0}'", name);
}
}
}
}
static bool verbose = false;
static bool force = false;
static int Main (string [] args)
{
bool machine = false;
for (int i = 0; i < args.Length; i++) {
switch (args [i]) {
case "-m":
case "--m":
machine = true;
break;
case "-v":
case "--v":
verbose = true;
break;
case "-f":
case "--f":
force = true;
break;
case "-help":
case "--help":
case "-?":
case "--?":
Help ();
return 0;
}
}
try {
X509Stores stores = ((machine) ? X509StoreManager.LocalMachine : X509StoreManager.CurrentUser);
// for all store (expect Untrusted)
UpdateStore (stores.TrustedRoot);
UpdateStore (stores.IntermediateCA);
UpdateStore (stores.Personal);
UpdateStore (stores.OtherPeople);
return 0;
}
catch (Exception e) {
Console.WriteLine ("ERROR: Unexpected exception: {0}", e);
return 1;
}
}
}
}