Software /
code /
prosody
Comparison
util/sasl/scram.lua @ 5843:fb6573e191cf
Merge Tobias SCRAM-PLUS work
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sun, 22 Sep 2013 00:44:20 +0200 |
parent | 5776:bd0ff8ae98a8 |
parent | 5841:1b0c7e7c6be8 |
child | 5844:4f545674b0bc |
comparison
equal
deleted
inserted
replaced
5827:ae16bf17785d | 5843:fb6573e191cf |
---|---|
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. | 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 | 13 |
14 local s_match = string.match; | 14 local s_match = string.match; |
15 local type = type | 15 local type = type |
16 local string = string | 16 local string = string |
17 local tostring = tostring; | |
17 local base64 = require "util.encodings".base64; | 18 local base64 = require "util.encodings".base64; |
18 local hmac_sha1 = require "util.hashes".hmac_sha1; | 19 local hmac_sha1 = require "util.hashes".hmac_sha1; |
19 local sha1 = require "util.hashes".sha1; | 20 local sha1 = require "util.hashes".sha1; |
20 local Hi = require "util.hashes".scram_Hi_sha1; | 21 local Hi = require "util.hashes".scram_Hi_sha1; |
21 local generate_uuid = require "util.uuid".generate; | 22 local generate_uuid = require "util.uuid".generate; |
37 scram_{MECH}: | 38 scram_{MECH}: |
38 -- MECH being a standard hash name (like those at IANA's hash registry) with '-' replaced with '_' | 39 -- MECH being a standard hash name (like those at IANA's hash registry) with '-' replaced with '_' |
39 function(username, realm) | 40 function(username, realm) |
40 return stored_key, server_key, iteration_count, salt, state; | 41 return stored_key, server_key, iteration_count, salt, state; |
41 end | 42 end |
43 | |
44 Supported Channel Binding Backends | |
45 | |
46 'tls-unique' according to RFC 5929 | |
42 ]] | 47 ]] |
43 | 48 |
44 local default_i = 4096 | 49 local default_i = 4096 |
45 | 50 |
46 local function bp( b ) | 51 local function bp( b ) |
106 end | 111 end |
107 | 112 |
108 local function scram_gen(hash_name, H_f, HMAC_f) | 113 local function scram_gen(hash_name, H_f, HMAC_f) |
109 local function scram_hash(self, message) | 114 local function scram_hash(self, message) |
110 if not self.state then self["state"] = {} end | 115 if not self.state then self["state"] = {} end |
116 local support_channel_binding = false; | |
117 if self.profile.cb then support_channel_binding = true; end | |
111 | 118 |
112 if type(message) ~= "string" or #message == 0 then return "failure", "malformed-request" end | 119 if type(message) ~= "string" or #message == 0 then return "failure", "malformed-request" end |
113 if not self.state.name then | 120 if not self.state.name then |
114 -- we are processing client_first_message | 121 -- we are processing client_first_message |
115 local client_first_message = message; | 122 local client_first_message = message; |
116 | 123 |
117 -- TODO: fail if authzid is provided, since we don't support them yet | 124 -- TODO: fail if authzid is provided, since we don't support them yet |
118 self.state["client_first_message"] = client_first_message; | 125 self.state["client_first_message"] = client_first_message; |
119 self.state["gs2_cbind_flag"], self.state["authzid"], self.state["name"], self.state["clientnonce"] | 126 self.state["gs2_cbind_flag"], self.state["gs2_cbind_name"], self.state["authzid"], self.state["name"], self.state["clientnonce"] |
120 = client_first_message:match("^(%a),(.*),n=(.*),r=([^,]*).*"); | 127 = client_first_message:match("^(%a)=?([%a%-]*),(.*),n=(.*),r=([^,]*).*"); |
121 | 128 |
122 -- we don't do any channel binding yet | 129 -- check for invalid gs2_flag_type start |
123 if self.state.gs2_cbind_flag ~= "n" and self.state.gs2_cbind_flag ~= "y" then | 130 local gs2_flag_type = string.sub(self.state.gs2_cbind_flag, 0, 1) |
124 return "failure", "malformed-request"; | 131 if gs2_flag_type ~= "y" and gs2_flag_type ~= "n" and gs2_flag_type ~= "p" then |
132 return "failure", "malformed-request", "The GS2 header has to start with 'y', 'n', or 'p'." | |
133 end | |
134 | |
135 if support_channel_binding then | |
136 if string.sub(self.state.gs2_cbind_flag, 0, 1) == "y" then | |
137 return "failure", "malformed-request"; | |
138 end | |
139 | |
140 -- check whether we support the proposed channel binding type | |
141 if not self.profile.cb[self.state.gs2_cbind_name] then | |
142 return "failure", "malformed-request", "Proposed channel binding type isn't supported."; | |
143 end | |
144 else | |
145 -- we don't support channelbinding, | |
146 if self.state.gs2_cbind_flag ~= "n" and self.state.gs2_cbind_flag ~= "y" then | |
147 return "failure", "malformed-request"; | |
148 end | |
125 end | 149 end |
126 | 150 |
127 if not self.state.name or not self.state.clientnonce then | 151 if not self.state.name or not self.state.clientnonce then |
128 return "failure", "malformed-request", "Channel binding isn't support at this time."; | 152 return "failure", "malformed-request", "Channel binding isn't support at this time."; |
129 end | 153 end |
179 | 203 |
180 if not self.state.proof or not self.state.nonce or not self.state.channelbinding then | 204 if not self.state.proof or not self.state.nonce or not self.state.channelbinding then |
181 return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message."; | 205 return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message."; |
182 end | 206 end |
183 | 207 |
208 if self.state.gs2_cbind_name then | |
209 -- we support channelbinding, so check if the value is valid | |
210 local client_gs2_header = base64.decode(self.state.channelbinding) | |
211 local our_client_gs2_header = "p="..self.state.gs2_cbind_name..","..self.state["authzid"]..","..self.profile.cb[self.state.gs2_cbind_name](self); | |
212 | |
213 if client_gs2_header ~= our_client_gs2_header then | |
214 return "failure", "malformed-request", "Invalid channel binding value."; | |
215 end | |
216 end | |
217 | |
184 if self.state.nonce ~= self.state.clientnonce..self.state.servernonce then | 218 if self.state.nonce ~= self.state.clientnonce..self.state.servernonce then |
185 return "failure", "malformed-request", "Wrong nonce in client-final-message."; | 219 return "failure", "malformed-request", "Wrong nonce in client-final-message."; |
186 end | 220 end |
187 | 221 |
188 local ServerKey = self.state.server_key; | 222 local ServerKey = self.state.server_key; |
206 end | 240 end |
207 | 241 |
208 function init(registerMechanism) | 242 function init(registerMechanism) |
209 local function registerSCRAMMechanism(hash_name, hash, hmac_hash) | 243 local function registerSCRAMMechanism(hash_name, hash, hmac_hash) |
210 registerMechanism("SCRAM-"..hash_name, {"plain", "scram_"..(hashprep(hash_name))}, scram_gen(hash_name:lower(), hash, hmac_hash)); | 244 registerMechanism("SCRAM-"..hash_name, {"plain", "scram_"..(hashprep(hash_name))}, scram_gen(hash_name:lower(), hash, hmac_hash)); |
245 | |
246 -- register channel binding equivalent | |
247 registerMechanism("SCRAM-"..hash_name.."-PLUS", {"plain", "scram_"..(hashprep(hash_name))}, scram_gen(hash_name:lower(), hash, hmac_hash), {"tls-unique"}); | |
211 end | 248 end |
212 | 249 |
213 registerSCRAMMechanism("SHA-1", sha1, hmac_sha1); | 250 registerSCRAMMechanism("SHA-1", sha1, hmac_sha1); |
214 end | 251 end |
215 | 252 |