ruby-gpgme
ruby-gpgme copied to clipboard
GPGME::Crypto#encrypt throws an Errno::EBADF exception
The following code always throws an Errno::EBADF exception after some loop iterations:
require 'gpgme'
puts GPGME::Engine.info.inspect
crypto = GPGME::Crypto.new(armor: false, symmetric: true, password: 'password')
(0..1000000).each do |n|
puts n
crypto.encrypt(GPGME::Data.from_str('data'))
end
I tried it with Ruby 2.0.0p353 and Ruby 2.0.0p576 on Ubuntu 12.04.5 LTS and Ubuntu 14.04.1 LTS:
$ rvm use ruby-2.0.0-p576@gpgme --create
$ gem install gpgme
...
Successfully installed mini_portile-0.6.0
...
Successfully installed gpgme-2.0.7
...
$ ruby test.rb
[#<GPGME::EngineInfo:0x000000014bda10 @protocol=0, @file_name="/usr/bin/gpg", @version="1.4.16", @req_version="1.4.0">, #<GPGME::EngineInfo:0x000000014bd970 @protocol=6, @file_name="/nonexistent", @version="1.0", @req_version="1.0">]
0
1
...
403
404
/home/clemens/.rvm/gems/ruby-2.0.0-p576@gpgme/gems/gpgme-2.0.7/lib/gpgme/ctx.rb:484:in `flush': Bad file descriptor (Errno::EBADF)
from /home/clemens/.rvm/gems/ruby-2.0.0-p576@gpgme/gems/gpgme-2.0.7/lib/gpgme/ctx.rb:484:in `pass_function'
from /home/clemens/.rvm/gems/ruby-2.0.0-p576@gpgme/gems/gpgme-2.0.7/lib/gpgme/ctx.rb:449:in `call'
from /home/clemens/.rvm/gems/ruby-2.0.0-p576@gpgme/gems/gpgme-2.0.7/lib/gpgme/ctx.rb:449:in `gpgme_op_encrypt'
from /home/clemens/.rvm/gems/ruby-2.0.0-p576@gpgme/gems/gpgme-2.0.7/lib/gpgme/ctx.rb:449:in `encrypt'
from /home/clemens/.rvm/gems/ruby-2.0.0-p576@gpgme/gems/gpgme-2.0.7/lib/gpgme/crypto.rb:99:in `block in encrypt'
from /home/clemens/.rvm/gems/ruby-2.0.0-p576@gpgme/gems/gpgme-2.0.7/lib/gpgme/ctx.rb:71:in `new'
from /home/clemens/.rvm/gems/ruby-2.0.0-p576@gpgme/gems/gpgme-2.0.7/lib/gpgme/crypto.rb:90:in `encrypt'
from test.rb:21:in `block in <main>'
from test.rb:19:in `each'
from test.rb:19:in `<main>'
Installed packages under Ubuntu 12.04.5 LTS
$ dpkg --list gnupg libgpgme11 libgpgme11-dev
...
ii gnupg 1.4.11-3ubuntu2.7 GNU privacy guard - a free PGP replacement
ii libgpgme11 1.2.0-1.4ubuntu2.1 GPGME - GnuPG Made Easy
ii libgpgme11-dev 1.2.0-1.4ubuntu2.1 GPGME - GnuPG Made Easy
Installed packages under Ubuntu 14.04.1 LTS
$ dpkg --list gnupg libgpgme11 libgpgme11-dev
...
ii gnupg 1.4.16-1ubuntu2.1 amd64 GNU privacy guard - a free PGP replacement
ii libgpgme11:amd64 1.4.3-0.1ubuntu5.1 amd64 GPGME - GnuPG Made Easy (library)
ii libgpgme11-dev 1.4.3-0.1ubuntu5.1 amd64 GPGME - GnuPG Made Easy (development files)
I'm still looking for a proper way to fix this, but for your use-case, a workaround would be to use a custom passphrase callback and call IO#close at the end:
def pass_function(pass, uid_hint, passphrase_info, prev_was_bad, fd)
io = IO.for_fd(fd, 'w')
io.puts 'password'
io.flush
io.close
end
crypto = GPGME::Crypto.new(armor: false, symmetric: true,
passphrase_callback: method(:pass_function))
However, I doubt that we can simply do that in our default callback, as other gpgme engines might assume the FD is never closed.
Closing the file handle in the passphrase callback solved the problem.
Thank you!