Added mcollective 2.3.1 package
[packages/trusty/mcollective.git] / plugins / mcollective / security / psk.rb
1 module MCollective
2   module Security
3     # Impliments message authentication using digests and shared keys
4     #
5     # You should configure a psk in the configuration file and all requests
6     # will be validated for authenticity with this.
7     #
8     # Serialization uses Marshal, this is the default security module that is
9     # supported out of the box.
10     #
11     # Validation is as default and is provided by MCollective::Security::Base
12     #
13     # You can configure the caller id being created, this can adjust how you
14     # create authorization plugins.  For example you can use a unix group instead
15     # of uid to do authorization.
16     class Psk < Base
17       require 'etc'
18
19       # Decodes a message by unserializing all the bits etc, it also validates
20       # it as valid using the psk etc
21       def decodemsg(msg)
22         body = Marshal.load(msg.payload)
23
24         should_process_msg?(msg, body[:requestid])
25
26         if validrequest?(body)
27           body[:body] = Marshal.load(body[:body])
28           return body
29         else
30           nil
31         end
32       end
33
34       # Encodes a reply
35       def encodereply(sender, msg, requestid, requestcallerid=nil)
36         serialized  = Marshal.dump(msg)
37         digest = makehash(serialized)
38
39         req = create_reply(requestid, sender, serialized)
40         req[:hash] = digest
41
42         Marshal.dump(req)
43       end
44
45       # Encodes a request msg
46       def encoderequest(sender, msg, requestid, filter, target_agent, target_collective, ttl=60)
47         serialized = Marshal.dump(msg)
48         digest = makehash(serialized)
49
50         req = create_request(requestid, filter, serialized, @initiated_by, target_agent, target_collective, ttl)
51         req[:hash] = digest
52
53         Marshal.dump(req)
54       end
55
56       # Checks the md5 hash in the request body against our psk, the request sent for validation
57       # should not have been deserialized already
58       def validrequest?(req)
59         digest = makehash(req[:body])
60
61         if digest == req[:hash]
62           @stats.validated
63
64           return true
65         else
66           @stats.unvalidated
67
68           raise(SecurityValidationFailed, "Received an invalid signature in message")
69         end
70       end
71
72       def callerid
73         if @config.pluginconf.include?("psk.callertype")
74           callertype = @config.pluginconf["psk.callertype"].to_sym if @config.pluginconf.include?("psk.callertype")
75         else
76           callertype = :uid
77         end
78
79         case callertype
80           when :gid
81             id  = "gid=#{Process.gid}"
82
83           when :group
84             raise "Cannot use the 'group' callertype for the PSK security plugin on the Windows platform" if Util.windows?
85
86             id = "group=#{Etc.getgrgid(Process.gid).name}"
87
88           when :user
89             id = "user=#{Etc.getlogin}"
90
91           when :identity
92             id = "identity=#{@config.identity}"
93
94           else
95             id ="uid=#{Process.uid}"
96         end
97
98         Log.debug("Setting callerid to #{id} based on callertype=#{callertype}")
99
100         id
101       end
102
103       private
104       # Retrieves the value of plugin.psk and builds a hash with it and the passed body
105       def makehash(body)
106         if ENV.include?("MCOLLECTIVE_PSK")
107           psk = ENV["MCOLLECTIVE_PSK"]
108         else
109           raise("No plugin.psk configuration option specified") unless @config.pluginconf.include?("psk")
110           psk = @config.pluginconf["psk"]
111         end
112
113         Digest::MD5.hexdigest(body.to_s + psk)
114       end
115     end
116   end
117 end