module MCollective module Security # Impliments message authentication using digests and shared keys # # You should configure a psk in the configuration file and all requests # will be validated for authenticity with this. # # Serialization uses Marshal, this is the default security module that is # supported out of the box. # # Validation is as default and is provided by MCollective::Security::Base # # You can configure the caller id being created, this can adjust how you # create authorization plugins. For example you can use a unix group instead # of uid to do authorization. class Psk < Base require 'etc' # Decodes a message by unserializing all the bits etc, it also validates # it as valid using the psk etc def decodemsg(msg) body = Marshal.load(msg.payload) should_process_msg?(msg, body[:requestid]) if validrequest?(body) body[:body] = Marshal.load(body[:body]) return body else nil end end # Encodes a reply def encodereply(sender, msg, requestid, requestcallerid=nil) serialized = Marshal.dump(msg) digest = makehash(serialized) req = create_reply(requestid, sender, serialized) req[:hash] = digest Marshal.dump(req) end # Encodes a request msg def encoderequest(sender, msg, requestid, filter, target_agent, target_collective, ttl=60) serialized = Marshal.dump(msg) digest = makehash(serialized) req = create_request(requestid, filter, serialized, @initiated_by, target_agent, target_collective, ttl) req[:hash] = digest Marshal.dump(req) end # Checks the md5 hash in the request body against our psk, the request sent for validation # should not have been deserialized already def validrequest?(req) digest = makehash(req[:body]) if digest == req[:hash] @stats.validated return true else @stats.unvalidated raise(SecurityValidationFailed, "Received an invalid signature in message") end end def callerid if @config.pluginconf.include?("psk.callertype") callertype = @config.pluginconf["psk.callertype"].to_sym if @config.pluginconf.include?("psk.callertype") else callertype = :uid end case callertype when :gid id = "gid=#{Process.gid}" when :group raise "Cannot use the 'group' callertype for the PSK security plugin on the Windows platform" if Util.windows? id = "group=#{Etc.getgrgid(Process.gid).name}" when :user id = "user=#{Etc.getlogin}" when :identity id = "identity=#{@config.identity}" else id ="uid=#{Process.uid}" end Log.debug("Setting callerid to #{id} based on callertype=#{callertype}") id end private # Retrieves the value of plugin.psk and builds a hash with it and the passed body def makehash(body) if ENV.include?("MCOLLECTIVE_PSK") psk = ENV["MCOLLECTIVE_PSK"] else raise("No plugin.psk configuration option specified") unless @config.pluginconf.include?("psk") psk = @config.pluginconf["psk"] end Digest::MD5.hexdigest(body.to_s + psk) end end end end