aboutsummaryrefslogtreecommitdiffstats
path: root/util/sasl
diff options
context:
space:
mode:
Diffstat (limited to 'util/sasl')
-rw-r--r--util/sasl/anonymous.lua6
-rw-r--r--util/sasl/external.lua2
-rw-r--r--util/sasl/oauthbearer.lua60
-rw-r--r--util/sasl/plain.lua10
-rw-r--r--util/sasl/scram.lua25
5 files changed, 81 insertions, 22 deletions
diff --git a/util/sasl/anonymous.lua b/util/sasl/anonymous.lua
index de98a5e2..be2c20d4 100644
--- a/util/sasl/anonymous.lua
+++ b/util/sasl/anonymous.lua
@@ -12,7 +12,7 @@
-- 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.
-local generate_random_id = require "util.id".medium;
+local generate_random_id = require "prosody.util.id".medium;
local _ENV = nil;
-- luacheck: std none
@@ -33,8 +33,8 @@ local function anonymous(self, message) -- luacheck: ignore 212/message
local username;
repeat
username = generate_random_id():lower();
- until self.profile.anonymous(self, username, self.realm);
- self.username = username;
+ self.username = username;
+ until self.profile.anonymous(self, username, self.realm, message);
return "success"
end
diff --git a/util/sasl/external.lua b/util/sasl/external.lua
index ce50743e..c3a3beb8 100644
--- a/util/sasl/external.lua
+++ b/util/sasl/external.lua
@@ -1,4 +1,4 @@
-local saslprep = require "util.encodings".stringprep.saslprep;
+local saslprep = require "prosody.util.encodings".stringprep.saslprep;
local _ENV = nil;
-- luacheck: std none
diff --git a/util/sasl/oauthbearer.lua b/util/sasl/oauthbearer.lua
new file mode 100644
index 00000000..36ba5be4
--- /dev/null
+++ b/util/sasl/oauthbearer.lua
@@ -0,0 +1,60 @@
+local json = require "prosody.util.json";
+local _ENV = nil;
+
+
+local function oauthbearer(self, message)
+ if not message then
+ return "failure", "malformed-request";
+ end
+
+ if message == "\001" then
+ return "failure", "not-authorized";
+ end
+
+ local gs2_header, kvpairs = message:match("^(n,[^,]*,)(.+)$");
+ if not gs2_header then
+ return "failure", "malformed-request";
+ end
+ local gs2_authzid = gs2_header:match("^[^,]*,a=([^,]*),$");
+
+ local auth_header;
+ for k, v in kvpairs:gmatch("([a-zA-Z]+)=([\033-\126 \009\r\n]*)\001") do
+ if k == "auth" then
+ auth_header = v;
+ break;
+ end
+ end
+
+ if not auth_header then
+ return "failure", "malformed-request";
+ end
+
+ local token = auth_header:match("^Bearer (.+)$");
+
+ local username, state, token_info = self.profile.oauthbearer(self, token, self.realm, gs2_authzid);
+
+ if state == false then
+ return "failure", "account-disabled";
+ elseif state == nil or not username then
+ -- For token-level errors, RFC 7628 demands use of a JSON-encoded
+ -- challenge response upon failure. We relay additional info from
+ -- the auth backend if available.
+ return "challenge", json.encode({
+ status = token_info and token_info.status or "invalid_token";
+ scope = token_info and token_info.scope or nil;
+ ["openid-configuration"] = token_info and token_info.oidc_discovery_url or nil;
+ });
+ end
+ self.username = username;
+ self.token_info = token_info;
+
+ return "success";
+end
+
+local function init(registerMechanism)
+ registerMechanism("OAUTHBEARER", {"oauthbearer"}, oauthbearer);
+end
+
+return {
+ init = init;
+}
diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua
index 43a66c5b..da867fb1 100644
--- a/util/sasl/plain.lua
+++ b/util/sasl/plain.lua
@@ -12,9 +12,9 @@
-- 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.
local s_match = string.match;
-local saslprep = require "util.encodings".stringprep.saslprep;
-local nodeprep = require "util.encodings".stringprep.nodeprep;
-local log = require "util.logger".init("sasl");
+local saslprep = require "prosody.util.encodings".stringprep.saslprep;
+local nodeprep = require "prosody.util.encodings".stringprep.nodeprep;
+local log = require "prosody.util.logger".init("sasl");
local _ENV = nil;
-- luacheck: std none
@@ -69,10 +69,10 @@ local function plain(self, message)
local correct, state = false, false;
if self.profile.plain then
local correct_password;
- correct_password, state = self.profile.plain(self, authentication, self.realm);
+ correct_password, state = self.profile.plain(self, authentication, self.realm, authorization);
correct = (saslprep(correct_password) == password);
elseif self.profile.plain_test then
- correct, state = self.profile.plain_test(self, authentication, password, self.realm);
+ correct, state = self.profile.plain_test(self, authentication, password, self.realm, authorization);
end
if state == false then
diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua
index 37abf4a4..ad279999 100644
--- a/util/sasl/scram.lua
+++ b/util/sasl/scram.lua
@@ -13,13 +13,13 @@
local s_match = string.match;
local type = type
-local base64 = require "util.encodings".base64;
-local hashes = require "util.hashes";
-local generate_uuid = require "util.uuid".generate;
-local saslprep = require "util.encodings".stringprep.saslprep;
-local nodeprep = require "util.encodings".stringprep.nodeprep;
-local log = require "util.logger".init("sasl");
-local binaryXOR = require "util.strbitop".sxor;
+local base64 = require "prosody.util.encodings".base64;
+local hashes = require "prosody.util.hashes";
+local generate_uuid = require "prosody.util.uuid".generate;
+local saslprep = require "prosody.util.encodings".stringprep.saslprep;
+local nodeprep = require "prosody.util.encodings".stringprep.nodeprep;
+local log = require "prosody.util.logger".init("sasl");
+local binaryXOR = require "prosody.util.strbitop".sxor;
local _ENV = nil;
-- luacheck: std none
@@ -101,7 +101,6 @@ local function scram_gen(hash_name, H_f, HMAC_f, get_auth_db, expect_cb)
local client_first_message = message;
-- TODO: fail if authzid is provided, since we don't support them yet
- -- luacheck: ignore 211/authzid
local gs2_header, gs2_cbind_flag, gs2_cbind_name, authzid, client_first_message_bare, username, clientnonce
= s_match(client_first_message, "^(([pny])=?([^,]*),([^,]*),)(m?=?[^,]*,?n=([^,]*),r=([^,]*),?.*)$");
@@ -112,8 +111,8 @@ local function scram_gen(hash_name, H_f, HMAC_f, get_auth_db, expect_cb)
if support_channel_binding and gs2_cbind_flag == "y" then
-- "y" -> client does support channel binding
-- but thinks the server does not.
- return "failure", "malformed-request";
- end
+ return "failure", "malformed-request";
+ end
if gs2_cbind_flag == "n" then
-- "n" -> client doesn't support channel binding.
@@ -144,7 +143,7 @@ local function scram_gen(hash_name, H_f, HMAC_f, get_auth_db, expect_cb)
-- retrieve credentials
local stored_key, server_key, salt, iteration_count;
if self.profile.plain then
- local password, status = self.profile.plain(self, username, self.realm)
+ local password, status = self.profile.plain(self, username, self.realm, authzid)
if status == nil then return "failure", "not-authorized"
elseif status == false then return "failure", "account-disabled" end
@@ -165,7 +164,7 @@ local function scram_gen(hash_name, H_f, HMAC_f, get_auth_db, expect_cb)
end
elseif self.profile[profile_name] then
local status;
- stored_key, server_key, iteration_count, salt, status = self.profile[profile_name](self, username, self.realm);
+ stored_key, server_key, iteration_count, salt, status = self.profile[profile_name](self, username, self.realm, authzid);
if status == nil then return "failure", "not-authorized"
elseif status == false then return "failure", "account-disabled" end
end
@@ -240,7 +239,7 @@ local function init(registerMechanism)
-- register channel binding equivalent
registerMechanism("SCRAM-"..hash_name.."-PLUS",
{"plain", "scram_"..(hashprep(hash_name))},
- scram_gen(hash_name:lower(), hash, hmac_hash, get_auth_db, true), {"tls-unique"});
+ scram_gen(hash_name:lower(), hash, hmac_hash, get_auth_db, true), {"tls-unique", "tls-exporter"});
end
registerSCRAMMechanism("SHA-1", hashes.sha1, hashes.hmac_sha1, hashes.pbkdf2_hmac_sha1);