~ubuntu-branches/ubuntu/quantal/puppet/quantal-security

« back to all changes in this revision

Viewing changes to .pc/CVE-2011-3872.patch/lib/puppet/network/xmlrpc/client.rb

  • Committer: Bazaar Package Importer
  • Author(s): Marc Deslauriers
  • Date: 2011-10-24 15:05:12 UTC
  • Revision ID: james.westby@ubuntu.com-20111024150512-yxqwfdp6hcs6of5l
Tags: 2.7.1-1ubuntu3.2
* SECURITY UPDATE: puppet master impersonation via incorrect certificates
  - debian/patches/CVE-2011-3872.patch: refactor certificate handling.
  - Thanks to upstream for providing the patch.
  - CVE-2011-3872

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
require 'puppet/sslcertificates'
 
2
require 'puppet/network/http_pool'
 
3
require 'openssl'
 
4
require 'puppet/external/base64'
 
5
 
 
6
require 'xmlrpc/client'
 
7
require 'net/https'
 
8
require 'yaml'
 
9
 
 
10
module Puppet::Network
 
11
  class ClientError < Puppet::Error; end
 
12
  class XMLRPCClientError < Puppet::Error; end
 
13
  class XMLRPCClient < ::XMLRPC::Client
 
14
 
 
15
    attr_accessor :puppet_server, :puppet_port
 
16
    @clients = {}
 
17
 
 
18
    class << self
 
19
      include Puppet::Util
 
20
      include Puppet::Util::ClassGen
 
21
    end
 
22
 
 
23
    # Create a netclient for each handler
 
24
    def self.mkclient(handler)
 
25
      interface = handler.interface
 
26
      namespace = interface.prefix
 
27
 
 
28
      # Create a subclass for every client type.  This is
 
29
      # so that all of the methods are on their own class,
 
30
      # so that their namespaces can define the same methods if
 
31
      # they want.
 
32
      constant = handler.name.to_s.capitalize
 
33
      name = namespace.downcase
 
34
      newclient = genclass(name, :hash => @clients, :constant => constant)
 
35
 
 
36
      interface.methods.each { |ary|
 
37
        method = ary[0]
 
38
        newclient.send(:define_method,method) { |*args|
 
39
          make_rpc_call(namespace, method, *args)
 
40
        }
 
41
      }
 
42
 
 
43
      newclient
 
44
    end
 
45
 
 
46
    def self.handler_class(handler)
 
47
      @clients[handler] || self.mkclient(handler)
 
48
    end
 
49
 
 
50
    class ErrorHandler
 
51
      def initialize(&block)
 
52
        singleton_class.define_method(:execute, &block)
 
53
      end
 
54
    end
 
55
 
 
56
    # Use a class variable so all subclasses have access to it.
 
57
    @@error_handlers = {}
 
58
 
 
59
    def self.error_handler(exception)
 
60
      if handler = @@error_handlers[exception.class]
 
61
        return handler
 
62
      else
 
63
        return @@error_handlers[:default]
 
64
      end
 
65
    end
 
66
 
 
67
    def self.handle_error(*exceptions, &block)
 
68
      handler = ErrorHandler.new(&block)
 
69
 
 
70
      exceptions.each do |exception|
 
71
        @@error_handlers[exception] = handler
 
72
      end
 
73
    end
 
74
 
 
75
    handle_error(OpenSSL::SSL::SSLError) do |client, detail, namespace, method|
 
76
      if detail.message =~ /bad write retry/
 
77
        Puppet.warning "Transient SSL write error; restarting connection and retrying"
 
78
        client.recycle_connection
 
79
        return :retry
 
80
      end
 
81
      ["certificate verify failed", "hostname was not match", "hostname not match"].each do |str|
 
82
        Puppet.warning "Certificate validation failed; consider using the certname configuration option" if detail.message.include?(str)
 
83
      end
 
84
      raise XMLRPCClientError, "Certificates were not trusted: #{detail}"
 
85
    end
 
86
 
 
87
    handle_error(:default) do |client, detail, namespace, method|
 
88
      if detail.message.to_s =~ /^Wrong size\. Was \d+, should be \d+$/
 
89
        Puppet.warning "XMLRPC returned wrong size.  Retrying."
 
90
        return :retry
 
91
      end
 
92
      Puppet.err "Could not call #{namespace}.#{method}: #{detail.inspect}"
 
93
      error = XMLRPCClientError.new(detail.to_s)
 
94
      error.set_backtrace detail.backtrace
 
95
      raise error
 
96
    end
 
97
 
 
98
    handle_error(OpenSSL::SSL::SSLError) do |client, detail, namespace, method|
 
99
      if detail.message =~ /bad write retry/
 
100
        Puppet.warning "Transient SSL write error; restarting connection and retrying"
 
101
        client.recycle_connection
 
102
        return :retry
 
103
      end
 
104
      ["certificate verify failed", "hostname was not match", "hostname not match"].each do |str|
 
105
        Puppet.warning "Certificate validation failed; consider using the certname configuration option" if detail.message.include?(str)
 
106
      end
 
107
      raise XMLRPCClientError, "Certificates were not trusted: #{detail}"
 
108
    end
 
109
 
 
110
    handle_error(::XMLRPC::FaultException) do |client, detail, namespace, method|
 
111
      raise XMLRPCClientError, detail.faultString
 
112
    end
 
113
 
 
114
    handle_error(Errno::ECONNREFUSED) do |client, detail, namespace, method|
 
115
      msg = "Could not connect to #{client.host} on port #{client.port}"
 
116
      raise XMLRPCClientError, msg
 
117
    end
 
118
 
 
119
    handle_error(SocketError) do |client, detail, namespace, method|
 
120
      Puppet.err "Could not find server #{@host}: #{detail}"
 
121
      error = XMLRPCClientError.new("Could not find server #{client.host}")
 
122
      error.set_backtrace detail.backtrace
 
123
      raise error
 
124
    end
 
125
 
 
126
    handle_error(Errno::EPIPE, EOFError) do |client, detail, namespace, method|
 
127
      Puppet.info "Other end went away; restarting connection and retrying"
 
128
      client.recycle_connection
 
129
      return :retry
 
130
    end
 
131
 
 
132
    handle_error(Timeout::Error) do |client, detail, namespace, method|
 
133
      Puppet.err "Connection timeout calling #{namespace}.#{method}: #{detail}"
 
134
      error = XMLRPCClientError.new("Connection Timeout")
 
135
      error.set_backtrace(detail.backtrace)
 
136
      raise error
 
137
    end
 
138
 
 
139
    def make_rpc_call(namespace, method, *args)
 
140
      Puppet.debug "Calling #{namespace}.#{method}"
 
141
      begin
 
142
        call("#{namespace}.#{method}",*args)
 
143
      rescue SystemExit,NoMemoryError
 
144
        raise
 
145
      rescue Exception => detail
 
146
        retry if self.class.error_handler(detail).execute(self, detail, namespace, method) == :retry
 
147
      end
 
148
    ensure
 
149
      http.finish if http.started?
 
150
    end
 
151
 
 
152
    def http
 
153
      @http ||= Puppet::Network::HttpPool.http_instance(host, port, true)
 
154
    end
 
155
 
 
156
    attr_reader :host, :port
 
157
 
 
158
    def initialize(hash = {})
 
159
      hash[:Path] ||= "/RPC2"
 
160
      hash[:Server] ||= Puppet[:server]
 
161
      hash[:Port] ||= Puppet[:masterport]
 
162
      hash[:HTTPProxyHost] ||= Puppet[:http_proxy_host]
 
163
      hash[:HTTPProxyPort] ||= Puppet[:http_proxy_port]
 
164
 
 
165
      if "none" == hash[:HTTPProxyHost]
 
166
        hash[:HTTPProxyHost] = nil
 
167
        hash[:HTTPProxyPort] = nil
 
168
      end
 
169
 
 
170
 
 
171
            super(
 
172
                
 
173
        hash[:Server],
 
174
        hash[:Path],
 
175
        hash[:Port],
 
176
        hash[:HTTPProxyHost],
 
177
        hash[:HTTPProxyPort],
 
178
        
 
179
        nil, # user
 
180
        nil, # password
 
181
        true, # use_ssl
 
182
        Puppet[:configtimeout] # use configured timeout (#1176)
 
183
      )
 
184
      @http = Puppet::Network::HttpPool.http_instance(@host, @port)
 
185
    end
 
186
 
 
187
    # Get rid of our existing connection, replacing it with a new one.
 
188
    # This should only happen if we lose our connection somehow (e.g., an EPIPE)
 
189
    # or we've just downloaded certs and we need to create new http instances
 
190
    # with the certs added.
 
191
    def recycle_connection
 
192
      http.finish if http.started?
 
193
      @http = nil
 
194
      self.http # force a new one
 
195
    end
 
196
 
 
197
    def start
 
198
        @http.start unless @http.started?
 
199
    rescue => detail
 
200
        Puppet.err "Could not connect to server: #{detail}"
 
201
    end
 
202
 
 
203
    def local
 
204
      false
 
205
    end
 
206
 
 
207
    def local?
 
208
      false
 
209
    end
 
210
  end
 
211
end