X-Git-Url: https://git.enpas.org/?a=blobdiff_plain;f=util%2Fsasl_cyrus.lua;h=002118fdc07c86f14b8f0c0686e8c70537e73db0;hb=0ef23c673d1d620c9c1cc3e8aed43add63634426;hp=980af598a6a2d2f5f0fd1d04b419bd38dc484972;hpb=25ad747ba1e296a10613b78abf5644ea3e959be0;p=prosody.git diff --git a/util/sasl_cyrus.lua b/util/sasl_cyrus.lua index 980af598..002118fd 100644 --- a/util/sasl_cyrus.lua +++ b/util/sasl_cyrus.lua @@ -13,20 +13,53 @@ local cyrussasl = require "cyrussasl"; local log = require "util.logger".init("sasl_cyrus"); -local array = require "util.array"; -local tostring = tostring; -local pairs, ipairs = pairs, ipairs; -local t_insert, t_concat = table.insert, table.concat; -local s_match = string.match; local setmetatable = setmetatable -local keys = keys; - -local print = print local pcall = pcall local s_match, s_gmatch = string.match, string.gmatch +local sasl_errstring = { + -- SASL result codes -- + [1] = "another step is needed in authentication"; + [0] = "successful result"; + [-1] = "generic failure"; + [-2] = "memory shortage failure"; + [-3] = "overflowed buffer"; + [-4] = "mechanism not supported"; + [-5] = "bad protocol / cancel"; + [-6] = "can't request info until later in exchange"; + [-7] = "invalid parameter supplied"; + [-8] = "transient failure (e.g., weak key)"; + [-9] = "integrity check failed"; + [-12] = "SASL library not initialized"; + + -- client only codes -- + [2] = "needs user interaction"; + [-10] = "server failed mutual authentication step"; + [-11] = "mechanism doesn't support requested feature"; + + -- server only codes -- + [-13] = "authentication failure"; + [-14] = "authorization failure"; + [-15] = "mechanism too weak for this user"; + [-16] = "encryption needed to use mechanism"; + [-17] = "One time use of a plaintext password will enable requested mechanism for user"; + [-18] = "passphrase expired, has to be reset"; + [-19] = "account disabled"; + [-20] = "user not found"; + [-23] = "version mismatch with plug-in"; + [-24] = "remote authentication server unavailable"; + [-26] = "user exists, but no verifier for user"; + + -- codes for password setting -- + [-21] = "passphrase locked"; + [-22] = "requested change was not needed"; + [-27] = "passphrase is too weak for security policy"; + [-28] = "user supplied passwords not permitted"; +}; +setmetatable(sasl_errstring, { __index = function() return "undefined error!" end }); + module "sasl_cyrus" local method = {}; @@ -39,56 +72,59 @@ local function init(service_name) if st then initialized = true; else - log("error", "Failed to initialize CyrusSASL: %s", errmsg); + log("error", "Failed to initialize Cyrus SASL: %s", errmsg); end end end -- create a new SASL object which can be used to authenticate clients -function new(realm, service_name) - local sasl_i = {}; +function new(realm, service_name, app_name) - init(service_name); + init(app_name or service_name); - sasl_i.realm = realm; - sasl_i.service_name = service_name; - sasl_i.cyrus = cyrussasl.server_new(service_name, nil, nil, nil, nil) - if sasl_i.cyrus == 0 then - log("error", "got NULL return value from server_new") + local st, ret = pcall(cyrussasl.server_new, service_name, nil, realm, nil, nil) + if not st then + log("error", "Creating SASL server connection failed: %s", ret); return nil; end + + local sasl_i = { realm = realm, service_name = service_name, cyrus = ret }; + + if cyrussasl.set_canon_cb then + local c14n_cb = function (user) + local node = s_match(user, "^([^@]+)"); + log("debug", "Canonicalizing username %s to %s", user, node) + return node + end + cyrussasl.set_canon_cb(sasl_i.cyrus, c14n_cb); + end + cyrussasl.setssf(sasl_i.cyrus, 0, 0xffffffff) - local s = setmetatable(sasl_i, method); - return s; + local mechanisms = {}; + local cyrus_mechs = cyrussasl.listmech(sasl_i.cyrus, nil, "", " ", ""); + for w in s_gmatch(cyrus_mechs, "[^ ]+") do + mechanisms[w] = true; + end + sasl_i.mechs = mechanisms; + return setmetatable(sasl_i, method); end --- get a fresh clone with the same realm, profiles and forbidden mechanisms +-- get a fresh clone with the same realm and service name function method:clean_clone() return new(self.realm, self.service_name) end --- set the forbidden mechanisms -function method:forbidden( restrict ) - log("debug", "Called method:forbidden. NOT IMPLEMENTED.") - return {} -end - -- get a list of possible SASL mechanims to use function method:mechanisms() - local mechanisms = {} - local cyrus_mechs = cyrussasl.listmech(self.cyrus, nil, "", " ", "") - for w in s_gmatch(cyrus_mechs, "[^ ]+") do - mechanisms[w] = true; - end - self.mechs = mechanisms - return array.collect(keys(mechanisms)); + return self.mechs; end -- select a mechanism to use function method:select(mechanism) - self.mechanism = mechanism; - if not self.mechs then self:mechanisms(); end - return self.mechs[mechanism]; + if not self.selected and self.mechs[mechanism] then + self.selected = mechanism; + return true; + end end -- feed new messages to process into the library @@ -96,8 +132,9 @@ function method:process(message) local err; local data; - if self.mechanism then - err, data = cyrussasl.server_start(self.cyrus, self.mechanism, message or "") + if not self.first_step_done then + err, data = cyrussasl.server_start(self.cyrus, self.selected, message or "") + self.first_step_done = true; else err, data = cyrussasl.server_step(self.cyrus, message or "") end @@ -105,21 +142,20 @@ function method:process(message) self.username = cyrussasl.get_username(self.cyrus) if (err == 0) then -- SASL_OK - return "success", data + if self.require_provisioning and not self.require_provisioning(self.username) then + return "failure", "not-authorized", "User authenticated successfully, but not provisioned for XMPP"; + end + return "success", data elseif (err == 1) then -- SASL_CONTINUE - return "challenge", data + return "challenge", data elseif (err == -4) then -- SASL_NOMECH - log("debug", "SASL mechanism not available from remote end") - return "failure", - "undefined-condition", - "SASL mechanism not available" + log("debug", "SASL mechanism not available from remote end") + return "failure", "invalid-mechanism", "SASL mechanism not available" elseif (err == -13) then -- SASL_BADAUTH - return "failure", "not-authorized", cyrussasl.get_message( self.cyrus ) + return "failure", "not-authorized", sasl_errstring[err]; else - log("debug", "Got SASL error condition %d", err) - return "failure", - "undefined-condition", - cyrussasl.get_message( self.cyrus ) + log("debug", "Got SASL error condition %d: %s", err, sasl_errstring[err]); + return "failure", "undefined-condition", sasl_errstring[err]; end end