Почему я могу перехватывать сообщения rb_warn в $ stderr только один раз?

#ruby #openssl

#ruby #openssl

Вопрос:

Из-за неправильного решения со стороны OpenSSL, которое повлияло на привязки Ruby, единственный способ проверить, подписан ли запрос OCSP, — это проанализировать предупреждение из OpenSSL::OCSP::Request#verify . Я перехватываю $stderr и читаю предупреждение, но если я повторяю этот процесс в нескольких модульных тестах, первое сообщение об ошибке фиксируется каждый раз, даже если при каждом перехвате используется новый буфер.

В качестве примера я создал этот скрипт: test.rb

 #!/usr/bin/env ruby

require 'openssl'
require 'stringio'

def main
  if ARGV[0]
    puts "signed: n#{signed}"
    puts "unsigned: n#{unsigned}"
  else
    puts "unsigned: n#{unsigned}"
    puts "signed: n#{signed}"
  end
end

def unsigned
  cert = OpenSSL::X509::Certificate.new
  certid = OpenSSL::OCSP::CertificateId.new cert, cert
  request = OpenSSL::OCSP::Request.new.add_certid certid
  store = OpenSSL::X509::Store.new

  capture_stderr { request.verify([], store) }
end

def signed
  key = OpenSSL::PKey::RSA.generate(2048)
  cert = OpenSSL::X509::Certificate.new
  cert.public_key = key.public_key
  cert.sign(key, OpenSSL::Digest::SHA1.new)
  certid = OpenSSL::OCSP::CertificateId.new OpenSSL::X509::Certificate.new, cert

  store = OpenSSL::X509::Store.new
  store.add_cert cert

  request = OpenSSL::OCSP::Request.new.add_certid certid
  request.sign(cert, key)

  capture_stderr { request.verify([], store) }
end

def capture_stderr
  $stderr = StringIO.new
  result = yield
  [result, $stderr.string]
ensure
  $stderr = STDERR
end

# try `./test.rb` and `./test.rb 1`
main
  

Изменяя порядок вызовов функций, я получаю разные результаты.

 $ ./test.rb
unsigned:
[false, "./test.rb:22: warning: error:27074080:OCSP routines:OCSP_request_verify:request not signedn"]
signed:
[false, "./test.rb:38: warning: error:27074080:OCSP routines:OCSP_request_verify:request not signedn"]
  

и

 $ ./test.rb 1
signed:
[false, "./test.rb:38: warning: error:27074065:OCSP routines:OCSP_request_verify:certificate verify errorn"]
unsigned:
[false, "./test.rb:22: warning: error:27074065:OCSP routines:OCSP_request_verify:certificate verify errorn"]
  

Я полагаю, что объяснение этого странного поведения, вероятно, относится к языковой части C кодовой базы Ruby stdlib.

Комментарии:

1. «… единственный способ проверить, подписан ли запрос OCSP, — это проанализировать предупреждение из OpenSSL ::OCSP::Request#verify» — Простите мое невежество… Вы можете найти источник для механизма OCSP в <openssl src>/apps/ocsp.c . Вы можете извлекать / повторно использовать любой код, который хотите, из библиотеки. Проблема, похоже, в Ruby и его неспособности предоставить то, что вам нужно. OpenSSL не предоставляет привязок; они предоставляются Python, Ruby, PHP и т. Д. Возможно, вам следует отправить отчет об ошибке в Ruby, чтобы получить то, что вам нужно.

2. Хм … раньше туда не заглядывал. Но там нет использования OCSP_request_verify . Проверяется только ответ. Так что, если это даже недоступно в части приложения, я думаю, это довольно сильное сообщение о том, что авторы OpenSSL не считают целесообразным проверять, откуда поступают запросы. Возможно, мне нужно переосмыслить, почему я это делаю (и почему OCSP RFC даже определяет статус SigRequired).

3. Если предупреждение или сообщение об ошибке поступает из OpenSSL, код может быть удален. OpenSSL предоставляет такие функции, как OCSP_verify , OCSP_BASICRESP_verify и OCSP_REQUEST_verify . Возможно, Ruby нужно его раскрыть. Не верьте мне на слово… cd <openssl src> а затем выполнить grep -IR 'OCSP_*.*verify' * .

Ответ №1:

Оказалось, что это ошибка в библиотеке OpenSSL Ruby. Это было исправлено в версии Gem 2.0.0-beta2: https://github.com/ruby/openssl/commit/9af69abcec15b114d9a0ec3811983fc1d7b5a1dc

Теперь сообщения OpenSSL не передаются повторно в stderr. Однако теперь невозможно отличить ненадежные подписи от отсутствующих подписей. К счастью, я ошибался раньше, и проверка действительно завершается ошибкой из-за отсутствия подписей.