Merge 0.10->trunk
[prosody.git] / util / sasl.lua
index 3eb2db65a2d882083faf1dbe4ef7d643423d3535..b91e29a6cd51c4c8da4b54e8e4fec318f4c20488 100644 (file)
@@ -27,19 +27,38 @@ Authentication Backend Prototypes:
 state = false : disabled
 state = true : enabled
 state = nil : non-existant
+
+Channel Binding:
+
+To enable support of channel binding in some mechanisms you need to provide appropriate callbacks in a table
+at profile.cb.
+
+Example:
+       profile.cb["tls-unique"] = function(self)
+               return self.user
+       end
+
 ]]
 
 local method = {};
 method.__index = method;
 local mechanisms = {};
 local backend_mechanism = {};
+local mechanism_channelbindings = {};
 
 -- register a new SASL mechanims
-local function registerMechanism(name, backends, f)
+function registerMechanism(name, backends, f, cb_backends)
        assert(type(name) == "string", "Parameter name MUST be a string.");
        assert(type(backends) == "string" or type(backends) == "table", "Parameter backends MUST be either a string or a table.");
        assert(type(f) == "function", "Parameter f MUST be a function.");
+       if cb_backends then assert(type(cb_backends) == "table"); end
        mechanisms[name] = f
+       if cb_backends then
+               mechanism_channelbindings[name] = {};
+               for _, cb_name in ipairs(cb_backends) do
+                       mechanism_channelbindings[name][cb_name] = true;
+               end
+       end
        for _, backend_name in ipairs(backends) do
                if backend_mechanism[backend_name] == nil then backend_mechanism[backend_name] = {}; end
                t_insert(backend_mechanism[backend_name], name);
@@ -48,17 +67,30 @@ end
 
 -- create a new SASL object which can be used to authenticate clients
 function new(realm, profile)
-       local mechanisms = {};
-       for backend, f in pairs(profile) do
-               if backend_mechanism[backend] then
-                       for _, mechanism in ipairs(backend_mechanism[backend]) do
-                               mechanisms[mechanism] = true;
+       local mechanisms = profile.mechanisms;
+       if not mechanisms then
+               mechanisms = {};
+               for backend, f in pairs(profile) do
+                       if backend_mechanism[backend] then
+                               for _, mechanism in ipairs(backend_mechanism[backend]) do
+                                       mechanisms[mechanism] = true;
+                               end
                        end
                end
+               profile.mechanisms = mechanisms;
        end
        return setmetatable({ profile = profile, realm = realm, mechs = mechanisms }, method);
 end
 
+-- add a channel binding handler
+function method:add_cb_handler(name, f)
+       if type(self.profile.cb) ~= "table" then
+               self.profile.cb = {};
+       end
+       self.profile.cb[name] = f;
+       return self;
+end
+
 -- get a fresh clone with the same realm and profile
 function method:clean_clone()
        return new(self.realm, self.profile)
@@ -66,23 +98,37 @@ end
 
 -- get a list of possible SASL mechanims to use
 function method:mechanisms()
-       return self.mechs;
+       local current_mechs = {};
+       for mech, _ in pairs(self.mechs) do
+               if mechanism_channelbindings[mech] then
+                       if self.profile.cb then
+                               local ok = false;
+                               for cb_name, _ in pairs(self.profile.cb) do
+                                       if mechanism_channelbindings[mech][cb_name] then
+                                               ok = true;
+                                       end
+                               end
+                               if ok == true then current_mechs[mech] = true; end
+                       end
+               else
+                       current_mechs[mech] = true;
+               end
+       end
+       return current_mechs;
 end
 
 -- select a mechanism to use
 function method:select(mechanism)
-       if self.mech_i then
-               return false;
+       if not self.selected and self.mechs[mechanism] then
+               self.selected = mechanism;
+               return true;
        end
-       
-       self.mech_i = mechanisms[self:mechanisms()[mechanism] and mechanism];
-       return (self.mech_i ~= nil);
 end
 
 -- feed new messages to process into the library
 function method:process(message)
        --if message == "" or message == nil then return "failure", "malformed-request" end
-       return self.mech_i(self, message);
+       return mechanisms[self.selected](self, message);
 end
 
 -- load the mechanisms
@@ -90,5 +136,6 @@ require "util.sasl.plain"     .init(registerMechanism);
 require "util.sasl.digest-md5".init(registerMechanism);
 require "util.sasl.anonymous" .init(registerMechanism);
 require "util.sasl.scram"     .init(registerMechanism);
+require "util.sasl.external"  .init(registerMechanism);
 
 return _M;