util/sasl_cyrus.lua
changeset 11856 71266f43699d
parent 11855 8f1e7fd55e7b
child 11857 ae5ac9830add
equal deleted inserted replaced
11855:8f1e7fd55e7b 11856:71266f43699d
     1 -- sasl.lua v0.4
       
     2 -- Copyright (C) 2008-2009 Tobias Markmann
       
     3 --
       
     4 --    All rights reserved.
       
     5 --
       
     6 --    Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
       
     7 --
       
     8 --        * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
       
     9 --        * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
       
    10 --        * Neither the name of Tobias Markmann nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
       
    11 --
       
    12 --    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    13 
       
    14 local cyrussasl = require "cyrussasl";
       
    15 local log = require "util.logger".init("sasl_cyrus");
       
    16 
       
    17 local setmetatable = setmetatable
       
    18 
       
    19 local pcall = pcall
       
    20 local s_match, s_gmatch = string.match, string.gmatch
       
    21 
       
    22 local sasl_errstring = {
       
    23 	-- SASL result codes --
       
    24 	[1]   = "another step is needed in authentication";
       
    25 	[0]   = "successful result";
       
    26 	[-1]  = "generic failure";
       
    27 	[-2]  = "memory shortage failure";
       
    28 	[-3]  = "overflowed buffer";
       
    29 	[-4]  = "mechanism not supported";
       
    30 	[-5]  = "bad protocol / cancel";
       
    31 	[-6]  = "can't request info until later in exchange";
       
    32 	[-7]  = "invalid parameter supplied";
       
    33 	[-8]  = "transient failure (e.g., weak key)";
       
    34 	[-9]  = "integrity check failed";
       
    35 	[-12] = "SASL library not initialized";
       
    36 
       
    37 	-- client only codes --
       
    38 	[2]   = "needs user interaction";
       
    39 	[-10] = "server failed mutual authentication step";
       
    40 	[-11] = "mechanism doesn't support requested feature";
       
    41 
       
    42 	-- server only codes --
       
    43 	[-13] = "authentication failure";
       
    44 	[-14] = "authorization failure";
       
    45 	[-15] = "mechanism too weak for this user";
       
    46 	[-16] = "encryption needed to use mechanism";
       
    47 	[-17] = "One time use of a plaintext password will enable requested mechanism for user";
       
    48 	[-18] = "passphrase expired, has to be reset";
       
    49 	[-19] = "account disabled";
       
    50 	[-20] = "user not found";
       
    51 	[-23] = "version mismatch with plug-in";
       
    52 	[-24] = "remote authentication server unavailable";
       
    53 	[-26] = "user exists, but no verifier for user";
       
    54 
       
    55 	-- codes for password setting --
       
    56 	[-21] = "passphrase locked";
       
    57 	[-22] = "requested change was not needed";
       
    58 	[-27] = "passphrase is too weak for security policy";
       
    59 	[-28] = "user supplied passwords not permitted";
       
    60 };
       
    61 setmetatable(sasl_errstring, { __index = function() return "undefined error!" end });
       
    62 
       
    63 local _ENV = nil;
       
    64 -- luacheck: std none
       
    65 
       
    66 local method = {};
       
    67 method.__index = method;
       
    68 local initialized = false;
       
    69 
       
    70 local function init(service_name)
       
    71 	if not initialized then
       
    72 		local st, errmsg = pcall(cyrussasl.server_init, service_name);
       
    73 		if st then
       
    74 			initialized = true;
       
    75 		else
       
    76 			log("error", "Failed to initialize Cyrus SASL: %s", errmsg);
       
    77 		end
       
    78 	end
       
    79 end
       
    80 
       
    81 -- create a new SASL object which can be used to authenticate clients
       
    82 -- host_fqdn may be nil in which case gethostname() gives the value.
       
    83 --      For GSSAPI, this determines the hostname in the service ticket (after
       
    84 --      reverse DNS canonicalization, only if [libdefaults] rdns = true which
       
    85 --      is the default).
       
    86 local function new(realm, service_name, app_name, host_fqdn)
       
    87 
       
    88 	init(app_name or service_name);
       
    89 
       
    90 	local st, ret = pcall(cyrussasl.server_new, service_name, host_fqdn, realm, nil, nil)
       
    91 	if not st then
       
    92 		log("error", "Creating SASL server connection failed: %s", ret);
       
    93 		return nil;
       
    94 	end
       
    95 
       
    96 	local sasl_i = { realm = realm, service_name = service_name, cyrus = ret };
       
    97 
       
    98 	if cyrussasl.set_canon_cb then
       
    99 		local c14n_cb = function (user)
       
   100 			local node = s_match(user, "^([^@]+)");
       
   101 			log("debug", "Canonicalizing username %s to %s", user, node)
       
   102 			return node
       
   103 		end
       
   104 		cyrussasl.set_canon_cb(sasl_i.cyrus, c14n_cb);
       
   105 	end
       
   106 
       
   107 	cyrussasl.setssf(sasl_i.cyrus, 0, 0xffffffff)
       
   108 	local mechanisms = {};
       
   109 	local cyrus_mechs = cyrussasl.listmech(sasl_i.cyrus, nil, "", " ", "");
       
   110 	for w in s_gmatch(cyrus_mechs, "[^ ]+") do
       
   111 		mechanisms[w] = true;
       
   112 	end
       
   113 	sasl_i.mechs = mechanisms;
       
   114 	return setmetatable(sasl_i, method);
       
   115 end
       
   116 
       
   117 -- get a fresh clone with the same realm and service name
       
   118 function method:clean_clone()
       
   119 	return new(self.realm, self.service_name)
       
   120 end
       
   121 
       
   122 -- get a list of possible SASL mechanims to use
       
   123 function method:mechanisms()
       
   124 	return self.mechs;
       
   125 end
       
   126 
       
   127 -- select a mechanism to use
       
   128 function method:select(mechanism)
       
   129 	if not self.selected and self.mechs[mechanism] then
       
   130 		self.selected = mechanism;
       
   131 		return true;
       
   132 	end
       
   133 end
       
   134 
       
   135 -- feed new messages to process into the library
       
   136 function method:process(message)
       
   137 	local err;
       
   138 	local data;
       
   139 
       
   140 	if not self.first_step_done then
       
   141 		err, data = cyrussasl.server_start(self.cyrus, self.selected, message or "")
       
   142 		self.first_step_done = true;
       
   143 	else
       
   144 		err, data = cyrussasl.server_step(self.cyrus, message or "")
       
   145 	end
       
   146 
       
   147 	self.username = cyrussasl.get_username(self.cyrus)
       
   148 
       
   149 	if (err == 0) then -- SASL_OK
       
   150 		if self.require_provisioning and not self.require_provisioning(self.username) then
       
   151 			return "failure", "not-authorized", "User authenticated successfully, but not provisioned for XMPP";
       
   152 		end
       
   153 		return "success", data
       
   154 	elseif (err == 1) then -- SASL_CONTINUE
       
   155 		return "challenge", data
       
   156 	elseif (err == -4) then -- SASL_NOMECH
       
   157 		log("debug", "SASL mechanism not available from remote end")
       
   158 		return "failure", "invalid-mechanism", "SASL mechanism not available"
       
   159 	elseif (err == -13) then -- SASL_BADAUTH
       
   160 		return "failure", "not-authorized", sasl_errstring[err];
       
   161 	else
       
   162 		log("debug", "Got SASL error condition %d: %s", err, sasl_errstring[err]);
       
   163 		return "failure", "undefined-condition", sasl_errstring[err];
       
   164 	end
       
   165 end
       
   166 
       
   167 return {
       
   168 	new = new;
       
   169 };