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