lib/r509/ocsp/responder/server.rb in r509-ocsp-responder-0.3.1 vs lib/r509/ocsp/responder/server.rb in r509-ocsp-responder-0.3.2
- old
+ new
@@ -1,169 +1,167 @@
-require 'rubygems' if RUBY_VERSION < "1.9"
require 'sinatra/base'
require 'r509'
require 'r509/ocsp/signer'
-require 'r509/validity/redis'
require 'base64'
require 'dependo'
require 'logger'
require 'time'
require File.dirname(__FILE__)+'/ocsp-config.rb'
# Capture USR2 calls so we can reload and print the config
# I'd rather use HUP, but daemons like thin already capture that
# so we can't use it.
Signal.trap("USR2") do
- R509::Ocsp::Responder::OcspConfig.load_config
- R509::Ocsp::Responder::OcspConfig.print_config
+ R509::OCSP::Responder::OCSPConfig.load_config
+ R509::OCSP::Responder::OCSPConfig.print_config
end
-module R509::Ocsp::Responder
- #error for status checking
- class StatusError < StandardError
- end
+module R509::OCSP::Responder
+ #error for status checking
+ class StatusError < StandardError
+ end
- class Server < Sinatra::Base
- include Dependo::Mixin
+ class Server < Sinatra::Base
+ include Dependo::Mixin
- configure do
- mime_type :ocsp, 'application/ocsp-response'
- disable :protection #disable Rack::Protection (for speed)
- disable :logging
- set :environment, :production
- end
+ configure do
+ mime_type :ocsp, 'application/ocsp-response'
+ disable :protection #disable Rack::Protection (for speed)
+ disable :logging
+ set :environment, :production
+ end
- error do
- log.error env["sinatra.error"].inspect
- log.error env["sinatra.error"].backtrace.join("\n")
- "Something is amiss with our OCSP responder. You should ... wait?"
- end
+ error do
+ log.error env["sinatra.error"].inspect
+ log.error env["sinatra.error"].backtrace.join("\n")
+ "Something is amiss with our OCSP responder. You should ... wait?"
+ end
- error OpenSSL::OCSP::OCSPError do
- "Invalid request"
- end
+ error OpenSSL::OCSP::OCSPError do
+ "Invalid request"
+ end
- error R509::Ocsp::Responder::StatusError do
- "Down"
- end
+ error R509::OCSP::Responder::StatusError do
+ "Down"
+ end
- get '/favicon.ico' do
- log.debug "go away. no children."
- "go away. no children"
- end
+ get '/favicon.ico' do
+ log.debug "go away. no children."
+ "go away. no children"
+ end
- get '/status/?' do
- begin
- if Dependo::Registry[:ocsp_signer].validity_checker.is_available?
- "OK"
- else
- raise R509::Ocsp::Responder::StatusError
- end
- rescue
- raise R509::Ocsp::Responder::StatusError
- end
+ get '/status/?' do
+ begin
+ if Dependo::Registry[:ocsp_signer].validity_checker.is_available?
+ "OK"
+ else
+ raise R509::OCSP::Responder::StatusError
end
+ rescue
+ raise R509::OCSP::Responder::StatusError
+ end
+ end
- get '/*' do
- raw_request = params[:splat].join("/")
- #remove any leading slashes (looking at you MS Crypto API)
- raw_request.sub!(/^\/+/,"")
- log.info { "GET Request: "+raw_request }
- der = Base64.decode64(raw_request)
- request_response = handle_ocsp_request(der, "GET")
- build_headers(request_response)
- request_response[:response].to_der
- end
+ get '/*' do
+ raw_request = params[:splat].join("/")
+ #remove any leading slashes (looking at you MS Crypto API)
+ raw_request.sub!(/^\/+/,"")
+ log.info { "GET Request: "+raw_request }
+ der = Base64.decode64(raw_request)
+ request_response = handle_ocsp_request(der, "GET")
+ build_headers(request_response)
+ request_response[:response].to_der
+ end
- post '/' do
- if request.media_type == 'application/ocsp-request'
- der = request.env["rack.input"].read
- log.info { "POST Request: "+Base64.encode64(der).gsub!(/\n/,"") }
- request_response = handle_ocsp_request(der, "POST")
- request_response[:response].to_der
- end
- end
+ post '/' do
+ if request.media_type == 'application/ocsp-request'
+ der = request.env["rack.input"].read
+ log.info { "POST Request: "+Base64.encode64(der).gsub!(/\n/,"") }
+ request_response = handle_ocsp_request(der, "POST")
+ request_response[:response].to_der
+ end
+ end
- private
+ private
- def handle_ocsp_request(der, method)
- begin
- request_response = ocsp_signer.handle_request(der)
+ def handle_ocsp_request(der, method)
+ begin
+ request_response = ocsp_signer.handle_request(der)
- log_ocsp_response(request_response[:response],method)
+ log_ocsp_response(request_response[:response],method)
- content_type :ocsp
- request_response
- rescue StandardError => e
- log.error "unexpected error #{e}"
- raise e
- end
- end
+ content_type :ocsp
+ request_response
+ rescue StandardError => e
+ log.error "unexpected error #{e}"
+ raise e
+ end
+ end
- def log_ocsp_response(ocsp_response, method="?")
- if response.nil?
- log.error "Something went horribly wrong"
- return
- end
+ def log_ocsp_response(ocsp_response, method="?")
+ if response.nil?
+ log.error "Something went horribly wrong"
+ return
+ end
- case ocsp_response.status
- when OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL
- serial_data = ocsp_response.basic.status.map do |status|
- friendly_status = case status[1]
- when 0
- "VALID"
- when 1
- "REVOKED"
- when 2
- "UNKNOWN"
- end
- if ocsp_response.basic.status[0][0].respond_to?(:issuer_key_hash)
- config_used = ocsp_signer.request_checker.configs_hash[ocsp_response.basic.status[0][0].issuer_key_hash]
- else
- config_used = ocsp_signer.request_checker.configs.find do |config|
- #we need to create an OCSP::CertificateId object that has the right
- #issuer so we can pass it to #cmp_issuer. This is annoying because
- #CertificateId wants a cert and its issuer, but we don't want to
- #force users to provide an end entity cert just to make this comparison
- #work. So, we create a fake new cert and pass it in.
- ee_cert = OpenSSL::X509::Certificate.new
- ee_cert.issuer = config.ca_cert.cert.subject
- issuer_certid = OpenSSL::OCSP::CertificateId.new(ee_cert,config.ca_cert.cert)
- ocsp_response.basic.status[0][0].cmp_issuer(issuer_certid)
- end
- end
- stats.record(config_used.ca_cert.subject.to_s, status[0].serial.to_s, friendly_status) if Dependo::Registry.has_key?(:stats)
- status[0].serial.to_s+" Status: #{friendly_status}"
- end
- log.info { "#{method} Request For Serial(s): #{serial_data.join(",")} UserAgent: #{env["HTTP_USER_AGENT"]}" }
- when OpenSSL::OCSP::RESPONSE_STATUS_UNAUTHORIZED
- log.info { "#{method} Request For Unauthorized CA. UserAgent: #{env["HTTP_USER_AGENT"]}" }
- when OpenSSL::OCSP::RESPONSE_STATUS_MALFORMEDREQUEST
- log.info { "#{method} Malformed Request. UserAgent: #{env["HTTP_USER_AGENT"]}" }
+ case ocsp_response.status
+ when OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL
+ serial_data = ocsp_response.basic.status.map do |status|
+ friendly_status = case status[1]
+ when 0
+ "VALID"
+ when 1
+ "REVOKED"
+ when 2
+ "UNKNOWN"
+ end
+ if ocsp_response.basic.status[0][0].respond_to?(:issuer_key_hash)
+ config_used = ocsp_signer.request_checker.configs_hash[ocsp_response.basic.status[0][0].issuer_key_hash]
+ else
+ config_used = ocsp_signer.request_checker.configs.find do |config|
+ #we need to create an OCSP::CertificateId object that has the right
+ #issuer so we can pass it to #cmp_issuer. This is annoying because
+ #CertificateId wants a cert and its issuer, but we don't want to
+ #force users to provide an end entity cert just to make this comparison
+ #work. So, we create a fake new cert and pass it in.
+ ee_cert = OpenSSL::X509::Certificate.new
+ ee_cert.issuer = config.ca_cert.cert.subject
+ issuer_certid = OpenSSL::OCSP::CertificateId.new(ee_cert,config.ca_cert.cert)
+ ocsp_response.basic.status[0][0].cmp_issuer(issuer_certid)
end
+ end
+ stats.record(config_used.ca_cert.subject.to_s, status[0].serial.to_s, friendly_status) if Dependo::Registry.has_key?(:stats)
+ status[0].serial.to_s+" Status: #{friendly_status}"
end
+ log.info { "#{method} Request For Serial(s): #{serial_data.join(",")} UserAgent: #{env["HTTP_USER_AGENT"]}" }
+ when OpenSSL::OCSP::RESPONSE_STATUS_UNAUTHORIZED
+ log.info { "#{method} Request For Unauthorized CA. UserAgent: #{env["HTTP_USER_AGENT"]}" }
+ when OpenSSL::OCSP::RESPONSE_STATUS_MALFORMEDREQUEST
+ log.info { "#{method} Malformed Request. UserAgent: #{env["HTTP_USER_AGENT"]}" }
+ end
+ end
- def build_headers(request_response)
- ocsp_response = request_response[:response]
- ocsp_request = request_response[:request]
+ def build_headers(request_response)
+ ocsp_response = request_response[:response]
+ ocsp_request = request_response[:request]
- # cache_headers is injected via config.ru
- # we only cache if it's a RESPONSE_STATUS_SUCCESSFUL response and there's no nonce.
- if cache_headers and not ocsp_response.basic.nil? and ocsp_response.check_nonce(ocsp_request) == R509::Ocsp::Request::Nonce::BOTH_ABSENT
- calculated_max_age = ocsp_response.basic.status[0][5] - Time.now
- #same with max_cache_age
- if not max_cache_age or ( max_cache_age > calculated_max_age )
- max_age = calculated_max_age
- else
- max_age = max_cache_age
- end
-
- response["Last-Modified"] = Time.now.httpdate
- response["ETag"] = OpenSSL::Digest::SHA1.new(ocsp_response.to_der).to_s
- response["Expires"] = ocsp_response.basic.status[0][5].httpdate
- response["Cache-Control"] = "max-age=#{max_age.to_i}, public, no-transform, must-revalidate"
- end
+ # cache_headers is injected via config.ru
+ # we only cache if it's a RESPONSE_STATUS_SUCCESSFUL response and there's no nonce.
+ if cache_headers and not ocsp_response.basic.nil? and ocsp_response.check_nonce(ocsp_request) == R509::OCSP::Request::Nonce::BOTH_ABSENT
+ calculated_max_age = ocsp_response.basic.status[0][5] - Time.now
+ #same with max_cache_age
+ if not max_cache_age or ( max_cache_age > calculated_max_age )
+ max_age = calculated_max_age
+ else
+ max_age = max_cache_age
end
+ response["Last-Modified"] = Time.now.httpdate
+ response["ETag"] = OpenSSL::Digest::SHA1.new(ocsp_response.to_der).to_s
+ response["Expires"] = ocsp_response.basic.status[0][5].httpdate
+ response["Cache-Control"] = "max-age=#{max_age.to_i}, public, no-transform, must-revalidate"
+ end
end
+
+ end
end