(MODULES-4686) fix incorrect GPG keys parsing on Debian 9
[puppet-modules/puppetlabs-apt.git] / lib / puppet / provider / apt_key / apt_key.rb
index f8c7d1976afb78b867e93148ca3e759f867bdb18..3f95c3cc96af9c68f40234d6f07a9a3072be5173 100644 (file)
@@ -26,15 +26,25 @@ Puppet::Type.type(:apt_key).provide(:apt_key) do
       key_output = apt_key(cli_args)
     end
 
-    pub_line, fpr_line = nil
+    pub_line, sub_line, fpr_line = nil
 
     key_array = key_output.split("\n").collect do |line|
       if line.start_with?('pub')
           pub_line = line
+          # reset fpr_line, to skip any previous subkeys which were collected
+          fpr_line = nil
+          sub_line = nil
+      elsif line.start_with?('sub')
+          sub_line = line
       elsif line.start_with?('fpr')
           fpr_line = line
       end
 
+      if (sub_line and fpr_line)
+        sub_line, fpr_line = nil
+        next
+      end
+
       next unless (pub_line and fpr_line)
 
       line_hash = key_line_hash(pub_line, fpr_line)
@@ -118,10 +128,24 @@ Puppet::Type.type(:apt_key).provide(:apt_key) do
     parsedValue = URI::parse(value)
     if parsedValue.scheme.nil?
       fail("The file #{value} does not exist") unless File.exists?(value)
-      value
+      # Because the tempfile method has to return a live object to prevent GC
+      # of the underlying file from occuring too early, we also have to return
+      # a file object here.  The caller can still call the #path method on the
+      # closed file handle to get the path.
+      f = File.open(value, 'r')
+      f.close
+      f
     else
       begin
-        key = parsedValue.read
+        # Only send basic auth if URL contains userinfo
+        # Some webservers (e.g. Amazon S3) return code 400 if empty basic auth is sent
+        if parsedValue.userinfo.nil?
+          key = parsedValue.read
+        else
+          user_pass = parsedValue.userinfo.split(':')
+          parsedValue.userinfo = ''
+          key = open(parsedValue, :http_basic_authentication => user_pass).read
+        end
       rescue OpenURI::HTTPError, Net::FTPPermError => e
         fail("#{e.message} for #{resource[:source]}")
       rescue SocketError
@@ -132,6 +156,9 @@ Puppet::Type.type(:apt_key).provide(:apt_key) do
     end
   end
 
+  # The tempfile method needs to return the tempfile object to the caller, so
+  # that it doesn't get deleted by the GC immediately after it returns.  We
+  # want the caller to control when it goes out of scope.
   def tempfile(content)
     file = Tempfile.new('apt_key')
     file.write content
@@ -155,7 +182,7 @@ Puppet::Type.type(:apt_key).provide(:apt_key) do
         warning('/usr/bin/gpg cannot be found for verification of the id.')
       end
     end
-    file.path
+    file
   end
 
   def exists?
@@ -173,9 +200,11 @@ Puppet::Type.type(:apt_key).provide(:apt_key) do
       end
       command.push('--recv-keys', resource[:id])
     elsif resource[:content]
-      command.push('add', tempfile(resource[:content]))
+      key_file = tempfile(resource[:content])
+      command.push('add', key_file.path)
     elsif resource[:source]
-      command.push('add', source_to_file(resource[:source]))
+      key_file = source_to_file(resource[:source])
+      command.push('add', key_file.path)
     # In case we really screwed up, better safe than sorry.
     else
       fail("an unexpected condition occurred while trying to add the key: #{resource[:id]}")