ruby-gpgme icon indicating copy to clipboard operation
ruby-gpgme copied to clipboard

GPGME::Crypto#encrypt throws an Errno::EBADF exception

Open vakuum opened this issue 10 years ago • 2 comments

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)

vakuum avatar Oct 17 '14 15:10 vakuum

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.

ueno avatar Oct 22 '14 10:10 ueno

Closing the file handle in the passphrase callback solved the problem.

Thank you!

vakuum avatar Oct 22 '14 15:10 vakuum