X-Git-Url: https://review.fuel-infra.org/gitweb?a=blobdiff_plain;ds=sidebyside;f=plugins%2Fmcollective%2Fsecurity%2Fpsk.rb;fp=plugins%2Fmcollective%2Fsecurity%2Fpsk.rb;h=56f596481eec53b379fcad01c2a85797053cc498;hb=b87d2f4e68281062df1913440ca5753ae63314a9;hp=0000000000000000000000000000000000000000;hpb=ab0ea530b8ac956091f17b104ab2311336cfc250;p=packages%2Fprecise%2Fmcollective.git diff --git a/plugins/mcollective/security/psk.rb b/plugins/mcollective/security/psk.rb new file mode 100644 index 0000000..56f5964 --- /dev/null +++ b/plugins/mcollective/security/psk.rb @@ -0,0 +1,117 @@ +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