2003-07-23 12:12:24 -04:00
|
|
|
#!/usr/bin/env ruby
|
|
|
|
|
|
|
|
require 'openssl'
|
2006-10-05 15:31:50 -04:00
|
|
|
require 'digest/md5'
|
2003-07-23 12:12:24 -04:00
|
|
|
|
|
|
|
class CHashDir
|
|
|
|
include Enumerable
|
|
|
|
|
|
|
|
def initialize(dirpath)
|
|
|
|
@dirpath = dirpath
|
|
|
|
@fingerprint_cache = @cert_cache = @crl_cache = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
def hash_dir(silent = false)
|
|
|
|
# ToDo: Should lock the directory...
|
|
|
|
@silent = silent
|
|
|
|
@fingerprint_cache = Hash.new
|
|
|
|
@cert_cache = Hash.new
|
|
|
|
@crl_cache = Hash.new
|
|
|
|
do_hash_dir
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_certs(name = nil)
|
|
|
|
if name
|
|
|
|
@cert_cache[hash_name(name)]
|
|
|
|
else
|
|
|
|
@cert_cache.values.flatten
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_crls(name = nil)
|
|
|
|
if name
|
|
|
|
@crl_cache[hash_name(name)]
|
|
|
|
else
|
|
|
|
@crl_cache.values.flatten
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def delete_crl(crl)
|
|
|
|
File.unlink(crl_filename(crl))
|
|
|
|
hash_dir(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
def add_crl(crl)
|
|
|
|
File.open(crl_filename(crl), "w") do |f|
|
|
|
|
f << crl.to_pem
|
|
|
|
end
|
|
|
|
hash_dir(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
def load_pem_file(filepath)
|
|
|
|
str = File.read(filepath)
|
|
|
|
begin
|
|
|
|
OpenSSL::X509::Certificate.new(str)
|
|
|
|
rescue
|
|
|
|
begin
|
2013-07-20 09:41:02 -04:00
|
|
|
OpenSSL::X509::CRL.new(str)
|
2003-07-23 12:12:24 -04:00
|
|
|
rescue
|
2013-07-20 09:41:02 -04:00
|
|
|
begin
|
|
|
|
OpenSSL::X509::Request.new(str)
|
|
|
|
rescue
|
|
|
|
nil
|
|
|
|
end
|
2003-07-23 12:12:24 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def crl_filename(crl)
|
|
|
|
path(hash_name(crl.issuer)) + '.pem'
|
|
|
|
end
|
|
|
|
|
|
|
|
def do_hash_dir
|
|
|
|
Dir.chdir(@dirpath) do
|
|
|
|
delete_symlink
|
|
|
|
Dir.glob('*.pem') do |pemfile|
|
2013-07-20 09:41:02 -04:00
|
|
|
cert = load_pem_file(pemfile)
|
|
|
|
case cert
|
|
|
|
when OpenSSL::X509::Certificate
|
|
|
|
link_hash_cert(pemfile, cert)
|
|
|
|
when OpenSSL::X509::CRL
|
|
|
|
link_hash_crl(pemfile, cert)
|
|
|
|
else
|
|
|
|
STDERR.puts("WARNING: #{pemfile} does not contain a certificate or CRL: skipping") unless @silent
|
|
|
|
end
|
2003-07-23 12:12:24 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def delete_symlink
|
|
|
|
Dir.entries(".").each do |entry|
|
|
|
|
next unless /^[\da-f]+\.r{0,1}\d+$/ =~ entry
|
|
|
|
File.unlink(entry) if FileTest.symlink?(entry)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def link_hash_cert(org_filename, cert)
|
|
|
|
name_hash = hash_name(cert.subject)
|
|
|
|
fingerprint = fingerprint(cert.to_der)
|
|
|
|
filepath = link_hash(org_filename, name_hash, fingerprint) { |idx|
|
|
|
|
"#{name_hash}.#{idx}"
|
|
|
|
}
|
|
|
|
unless filepath
|
|
|
|
unless @silent
|
2013-07-20 09:41:02 -04:00
|
|
|
STDERR.puts("WARNING: Skipping duplicate certificate #{org_filename}")
|
2003-07-23 12:12:24 -04:00
|
|
|
end
|
|
|
|
else
|
|
|
|
(@cert_cache[name_hash] ||= []) << path(filepath)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def link_hash_crl(org_filename, crl)
|
|
|
|
name_hash = hash_name(crl.issuer)
|
|
|
|
fingerprint = fingerprint(crl.to_der)
|
|
|
|
filepath = link_hash(org_filename, name_hash, fingerprint) { |idx|
|
|
|
|
"#{name_hash}.r#{idx}"
|
|
|
|
}
|
|
|
|
unless filepath
|
|
|
|
unless @silent
|
2013-07-20 09:41:02 -04:00
|
|
|
STDERR.puts("WARNING: Skipping duplicate CRL #{org_filename}")
|
2003-07-23 12:12:24 -04:00
|
|
|
end
|
|
|
|
else
|
|
|
|
(@crl_cache[name_hash] ||= []) << path(filepath)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def link_hash(org_filename, name, fingerprint)
|
|
|
|
idx = 0
|
|
|
|
filepath = nil
|
|
|
|
while true
|
|
|
|
filepath = yield(idx)
|
|
|
|
break unless FileTest.symlink?(filepath) or FileTest.exist?(filepath)
|
|
|
|
if @fingerprint_cache[filepath] == fingerprint
|
2013-07-20 09:41:02 -04:00
|
|
|
return false
|
2003-07-23 12:12:24 -04:00
|
|
|
end
|
|
|
|
idx += 1
|
|
|
|
end
|
|
|
|
STDOUT.puts("#{org_filename} => #{filepath}") unless @silent
|
|
|
|
symlink(org_filename, filepath)
|
|
|
|
@fingerprint_cache[filepath] = fingerprint
|
|
|
|
filepath
|
|
|
|
end
|
|
|
|
|
|
|
|
def symlink(from, to)
|
|
|
|
begin
|
|
|
|
File.symlink(from, to)
|
|
|
|
rescue
|
|
|
|
File.open(to, "w") do |f|
|
2013-07-20 09:41:02 -04:00
|
|
|
f << File.read(from)
|
2003-07-23 12:12:24 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def path(filename)
|
|
|
|
File.join(@dirpath, filename)
|
|
|
|
end
|
|
|
|
|
|
|
|
def hash_name(name)
|
|
|
|
sprintf("%x", name.hash)
|
|
|
|
end
|
|
|
|
|
|
|
|
def fingerprint(der)
|
2020-04-19 11:14:36 -04:00
|
|
|
Digest.hexdigest('MD5', der).upcase
|
2003-07-23 12:12:24 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if $0 == __FILE__
|
|
|
|
dirlist = ARGV
|
|
|
|
dirlist << '/usr/ssl/certs' if dirlist.empty?
|
|
|
|
dirlist.each do |dir|
|
|
|
|
CHashDir.new(dir).hash_dir
|
|
|
|
end
|
|
|
|
end
|