Software /
code /
prosody
Comparison
util/sasl/scram.lua @ 3099:2c4d06e7e3d3
util.sasl.scram: Check nonce in client final message. Check channel binding flag in client first message. Adding some TODOs on more strict parsing. (thanks Marc Santamaria)
author | Tobias Markmann <tm@ayena.de> |
---|---|
date | Sat, 22 May 2010 01:48:31 +0200 |
parent | 3098:e5d349c0acde |
child | 3100:6731dff05c99 |
comparison
equal
deleted
inserted
replaced
3098:e5d349c0acde | 3099:2c4d06e7e3d3 |
---|---|
1 -- sasl.lua v0.4 | 1 -- sasl.lua v0.4 |
2 -- Copyright (C) 2008-2010 Tobias Markmann | 2 -- Copyright (C) 2008-2010 Tobias Markmann |
3 -- | 3 -- |
4 -- All rights reserved. | 4 -- All rights reserved. |
5 -- | 5 -- |
6 -- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: | 6 -- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: |
7 -- | 7 -- |
8 -- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. | 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. | 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. | 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 -- | 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. | 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 base64 = require "util.encodings".base64; | 17 local base64 = require "util.encodings".base64; |
97 if not self.state then self["state"] = {} end | 97 if not self.state then self["state"] = {} end |
98 | 98 |
99 if not self.state.name then | 99 if not self.state.name then |
100 -- we are processing client_first_message | 100 -- we are processing client_first_message |
101 local client_first_message = message; | 101 local client_first_message = message; |
102 | |
103 -- TODO: more strict parsing of client_first_message | |
104 -- TODO: fail if authzid is provided, since we don't support them yet | |
102 self.state["client_first_message"] = client_first_message; | 105 self.state["client_first_message"] = client_first_message; |
103 self.state["name"] = client_first_message:match("n=(.+),r=") | 106 self.state["name"] = client_first_message:match("n=(.+),r=") |
104 self.state["clientnonce"] = client_first_message:match("r=([^,]+)") | 107 self.state["clientnonce"] = client_first_message:match("r=([^,]+)") |
105 | 108 self.state["gs2_cbind_flag"] = client_first_message:sub(1, 1) |
109 -- we don't do any channel binding yet | |
110 if self.state.gs2_cbind_flag ~= "n" and self.state.gs2_cbind_flag ~= "y" then | |
111 return "failure", "malformed-request"; | |
112 end | |
113 | |
106 if not self.state.name or not self.state.clientnonce then | 114 if not self.state.name or not self.state.clientnonce then |
107 return "failure", "malformed-request"; | 115 return "failure", "malformed-request", "Channel binding isn't support at this time."; |
108 end | 116 end |
109 | 117 |
110 self.state.name = validate_username(self.state.name); | 118 self.state.name = validate_username(self.state.name); |
111 if not self.state.name then | 119 if not self.state.name then |
112 log("debug", "Username violates either SASLprep or contains forbidden character sequences.") | 120 log("debug", "Username violates either SASLprep or contains forbidden character sequences.") |
144 return "challenge", server_first_message | 152 return "challenge", server_first_message |
145 else | 153 else |
146 if type(message) ~= "string" then return "failure", "malformed-request" end | 154 if type(message) ~= "string" then return "failure", "malformed-request" end |
147 -- we are processing client_final_message | 155 -- we are processing client_final_message |
148 local client_final_message = message; | 156 local client_final_message = message; |
149 | 157 |
158 -- TODO: more strict parsing of client_final_message | |
150 self.state["proof"] = client_final_message:match("p=(.+)"); | 159 self.state["proof"] = client_final_message:match("p=(.+)"); |
151 self.state["nonce"] = client_final_message:match("r=(.+),p="); | 160 self.state["nonce"] = client_final_message:match("r=(.+),p="); |
152 self.state["channelbinding"] = client_final_message:match("c=(.+),r="); | 161 self.state["channelbinding"] = client_final_message:match("c=(.+),r="); |
162 | |
153 if not self.state.proof or not self.state.nonce or not self.state.channelbinding then | 163 if not self.state.proof or not self.state.nonce or not self.state.channelbinding then |
154 return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message."; | 164 return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message."; |
155 end | 165 end |
156 | 166 |
167 if self.state.nonce ~= self.state.servernonce then | |
168 return "failure", "malformed-request", "Wrong nonce in client-second-message."; | |
169 end | |
170 | |
157 local SaltedPassword = self.state.salted_password; | 171 local SaltedPassword = self.state.salted_password; |
158 local ClientKey = HMAC_f(SaltedPassword, "Client Key") | 172 local ClientKey = HMAC_f(SaltedPassword, "Client Key") |
159 local ServerKey = HMAC_f(SaltedPassword, "Server Key") | 173 local ServerKey = HMAC_f(SaltedPassword, "Server Key") |
160 local StoredKey = H_f(ClientKey) | 174 local StoredKey = H_f(ClientKey) |
161 local AuthMessage = "n=" .. s_match(self.state.client_first_message,"n=(.+)") .. "," .. self.state.server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+") | 175 local AuthMessage = "n=" .. s_match(self.state.client_first_message,"n=(.+)") .. "," .. self.state.server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+") |
162 local ClientSignature = HMAC_f(StoredKey, AuthMessage) | 176 local ClientSignature = HMAC_f(StoredKey, AuthMessage) |
163 local ClientProof = binaryXOR(ClientKey, ClientSignature) | 177 local ClientProof = binaryXOR(ClientKey, ClientSignature) |
164 local ServerSignature = HMAC_f(ServerKey, AuthMessage) | 178 local ServerSignature = HMAC_f(ServerKey, AuthMessage) |
165 | 179 |
166 if base64.encode(ClientProof) == self.state.proof then | 180 if base64.encode(ClientProof) == self.state.proof then |
167 local server_final_message = "v="..base64.encode(ServerSignature); | 181 local server_final_message = "v="..base64.encode(ServerSignature); |
168 self["username"] = self.state.name; | 182 self["username"] = self.state.name; |
169 return "success", server_final_message; | 183 return "success", server_final_message; |
170 else | 184 else |
177 | 191 |
178 function init(registerMechanism) | 192 function init(registerMechanism) |
179 local function registerSCRAMMechanism(hash_name, hash, hmac_hash) | 193 local function registerSCRAMMechanism(hash_name, hash, hmac_hash) |
180 registerMechanism("SCRAM-"..hash_name, {"plain", "scram_"..(hash_name:lower())}, scram_gen(hash_name:lower(), hash, hmac_hash)); | 194 registerMechanism("SCRAM-"..hash_name, {"plain", "scram_"..(hash_name:lower())}, scram_gen(hash_name:lower(), hash, hmac_hash)); |
181 end | 195 end |
182 | 196 |
183 registerSCRAMMechanism("SHA-1", sha1, hmac_sha1); | 197 registerSCRAMMechanism("SHA-1", sha1, hmac_sha1); |
184 end | 198 end |
185 | 199 |
186 return _M; | 200 return _M; |