-rw-r--r-- | lib/basic_rp.cc | 29 |
1 files changed, 27 insertions, 2 deletions
diff --git a/lib/basic_rp.cc b/lib/basic_rp.cc index e65d9fb..3357d0b 100644 --- a/lib/basic_rp.cc +++ b/lib/basic_rp.cc | |||
@@ -1,45 +1,64 @@ | |||
1 | #include <cassert> | 1 | #include <cassert> |
2 | #include <openssl/sha.h> | 2 | #include <openssl/sha.h> |
3 | #include <openssl/hmac.h> | 3 | #include <openssl/hmac.h> |
4 | #include <opkele/basic_rp.h> | 4 | #include <opkele/basic_rp.h> |
5 | #include <opkele/exception.h> | 5 | #include <opkele/exception.h> |
6 | #include <opkele/uris.h> | 6 | #include <opkele/uris.h> |
7 | #include <opkele/data.h> | 7 | #include <opkele/data.h> |
8 | #include <opkele/util.h> | 8 | #include <opkele/util.h> |
9 | #include <opkele/util-internal.h> | 9 | #include <opkele/util-internal.h> |
10 | #include <opkele/curl.h> | 10 | #include <opkele/curl.h> |
11 | #include <opkele/debug.h> | ||
11 | 12 | ||
12 | namespace opkele { | 13 | namespace opkele { |
13 | 14 | ||
15 | void basic_RP::reset_vars() { | ||
16 | claimed_id.clear(); identity.clear(); | ||
17 | } | ||
18 | |||
19 | const string& basic_RP::get_claimed_id() const { | ||
20 | if(claimed_id.empty()) | ||
21 | throw non_identity(OPKELE_CP_ "attempting to retreive claimed_id of non-identity assertion"); | ||
22 | assert(!identity.empty()); | ||
23 | return claimed_id; | ||
24 | } | ||
25 | |||
26 | const string& basic_RP::get_identity() const { | ||
27 | if(identity.empty()) | ||
28 | throw non_identity(OPKELE_CP_ "attempting to retrieve identity of non-identity related assertion"); | ||
29 | assert(!claimed_id.empty()); | ||
30 | return identity; | ||
31 | } | ||
32 | |||
14 | static void dh_get_secret( | 33 | static void dh_get_secret( |
15 | secret_t& secret, const basic_openid_message& om, | 34 | secret_t& secret, const basic_openid_message& om, |
16 | const char *exp_assoc, const char *exp_sess, | 35 | const char *exp_assoc, const char *exp_sess, |
17 | util::dh_t& dh, | 36 | util::dh_t& dh, |
18 | size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*), | 37 | size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*), |
19 | size_t exp_s_len) try { | 38 | size_t exp_s_len) try { |
20 | if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess) | 39 | if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess) |
21 | throw bad_input(OPKELE_CP_ "Unexpected associate response"); | 40 | throw bad_input(OPKELE_CP_ "Unexpected associate response"); |
22 | util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public")); | 41 | util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public")); |
23 | vector<unsigned char> ck(DH_size(dh)+1); | 42 | vector<unsigned char> ck(DH_size(dh)+1); |
24 | unsigned char *ckptr = &(ck.front())+1; | 43 | unsigned char *ckptr = &(ck.front())+1; |
25 | int cklen = DH_compute_key(ckptr,s_pub,dh); | 44 | int cklen = DH_compute_key(ckptr,s_pub,dh); |
26 | if(cklen<0) | 45 | if(cklen<0) |
27 | throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); | 46 | throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); |
28 | if(cklen && (*ckptr)&0x80) { | 47 | if(cklen && (*ckptr)&0x80) { |
29 | (*(--ckptr))=0; ++cklen; } | 48 | (*(--ckptr))=0; ++cklen; } |
30 | assert(d_len<=SHA256_DIGEST_LENGTH); | 49 | assert(d_len<=SHA256_DIGEST_LENGTH); |
31 | unsigned char key_digest[SHA256_DIGEST_LENGTH]; | 50 | unsigned char key_digest[SHA256_DIGEST_LENGTH]; |
32 | secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key")); | 51 | secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key")); |
33 | if(secret.size()!=exp_s_len) | 52 | if(secret.size()!=exp_s_len) |
34 | throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type"); | 53 | throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type"); |
35 | }catch(opkele::failed_lookup& ofl) { | 54 | }catch(opkele::failed_lookup& ofl) { |
36 | throw bad_input(OPKELE_CP_ "Incoherent response from OP"); | 55 | throw bad_input(OPKELE_CP_ "Incoherent response from OP"); |
37 | } OPKELE_RETHROW | 56 | } OPKELE_RETHROW |
38 | 57 | ||
39 | static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) { | 58 | static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) { |
40 | util::curl_pick_t curl = util::curl_pick_t::easy_init(); | 59 | util::curl_pick_t curl = util::curl_pick_t::easy_init(); |
41 | if(!curl) | 60 | if(!curl) |
42 | throw exception_curl(OPKELE_CP_ "failed to initialize curl"); | 61 | throw exception_curl(OPKELE_CP_ "failed to initialize curl"); |
43 | string request = inm.query_string(); | 62 | string request = inm.query_string(); |
44 | CURLcode r; | 63 | CURLcode r; |
45 | (r=curl.misc_sets()) | 64 | (r=curl.misc_sets()) |
@@ -167,64 +186,65 @@ namespace opkele { | |||
167 | return signeds.end(); } | 186 | return signeds.end(); } |
168 | }; | 187 | }; |
169 | 188 | ||
170 | static void parse_query(const string& u,string::size_type q, | 189 | static void parse_query(const string& u,string::size_type q, |
171 | map<string,string>& p) { | 190 | map<string,string>& p) { |
172 | if(q==string::npos) | 191 | if(q==string::npos) |
173 | return; | 192 | return; |
174 | assert(u[q]=='?'); | 193 | assert(u[q]=='?'); |
175 | ++q; | 194 | ++q; |
176 | string::size_type l = u.size(); | 195 | string::size_type l = u.size(); |
177 | while(q<l) { | 196 | while(q<l) { |
178 | string::size_type eq = u.find('=',q); | 197 | string::size_type eq = u.find('=',q); |
179 | string::size_type am = u.find('&',q); | 198 | string::size_type am = u.find('&',q); |
180 | if(am==string::npos) { | 199 | if(am==string::npos) { |
181 | if(eq==string::npos) { | 200 | if(eq==string::npos) { |
182 | p[""] = u.substr(q); | 201 | p[""] = u.substr(q); |
183 | }else{ | 202 | }else{ |
184 | p[u.substr(q,eq-q)] = u.substr(eq+1); | 203 | p[u.substr(q,eq-q)] = u.substr(eq+1); |
185 | } | 204 | } |
186 | break; | 205 | break; |
187 | }else{ | 206 | }else{ |
188 | if(eq==string::npos || eq>am) { | 207 | if(eq==string::npos || eq>am) { |
189 | p[""] = u.substr(q,eq-q); | 208 | p[""] = u.substr(q,eq-q); |
190 | }else{ | 209 | }else{ |
191 | p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1); | 210 | p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1); |
192 | } | 211 | } |
193 | q = ++am; | 212 | q = ++am; |
194 | } | 213 | } |
195 | } | 214 | } |
196 | } | 215 | } |
197 | 216 | ||
198 | void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) { | 217 | void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) { |
218 | reset_vars(); | ||
199 | bool o2 = om.has_field("ns") | 219 | bool o2 = om.has_field("ns") |
200 | && om.get_field("ns")==OIURI_OPENID20; | 220 | && om.get_field("ns")==OIURI_OPENID20; |
201 | if( (!o2) && om.has_field("user_setup_url")) | 221 | if( (!o2) && om.has_field("user_setup_url")) |
202 | throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided", | 222 | throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided", |
203 | om.get_field("user_setup_url")); | 223 | om.get_field("user_setup_url")); |
204 | string m = om.get_field("mode"); | 224 | string m = om.get_field("mode"); |
205 | if(o2 && m=="setup_needed") | 225 | if(o2 && m=="setup_needed") |
206 | throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided"); | 226 | throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided"); |
207 | if(m=="cancel") | 227 | if(m=="cancel") |
208 | throw id_res_cancel(OPKELE_CP_ "authentication cancelled"); | 228 | throw id_res_cancel(OPKELE_CP_ "authentication cancelled"); |
209 | bool go_dumb=false; | 229 | bool go_dumb=false; |
210 | try { | 230 | try { |
211 | string OP = o2 | 231 | string OP = o2 |
212 | ?om.get_field("op_endpoint") | 232 | ?om.get_field("op_endpoint") |
213 | :get_endpoint().uri; | 233 | :get_endpoint().uri; |
214 | assoc_t assoc = retrieve_assoc( | 234 | assoc_t assoc = retrieve_assoc( |
215 | OP,om.get_field("assoc_handle")); | 235 | OP,om.get_field("assoc_handle")); |
216 | if(om.get_field("sig")!=util::base64_signature(assoc,om)) | 236 | if(om.get_field("sig")!=util::base64_signature(assoc,om)) |
217 | throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); | 237 | throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); |
218 | }catch(dumb_RP& drp) { | 238 | }catch(dumb_RP& drp) { |
219 | go_dumb=true; | 239 | go_dumb=true; |
220 | }catch(failed_lookup& e) { | 240 | }catch(failed_lookup& e) { |
221 | go_dumb=true; | 241 | go_dumb=true; |
222 | } OPKELE_RETHROW | 242 | } OPKELE_RETHROW |
223 | if(go_dumb) { | 243 | if(go_dumb) { |
224 | try { | 244 | try { |
225 | string OP = o2 | 245 | string OP = o2 |
226 | ?om.get_field("op_endpoint") | 246 | ?om.get_field("op_endpoint") |
227 | :get_endpoint().uri; | 247 | :get_endpoint().uri; |
228 | check_authentication(OP,om); | 248 | check_authentication(OP,om); |
229 | }catch(failed_check_authentication& fca) { | 249 | }catch(failed_check_authentication& fca) { |
230 | throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); | 250 | throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); |
@@ -242,59 +262,64 @@ namespace opkele { | |||
242 | throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs"); | 262 | throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs"); |
243 | } | 263 | } |
244 | if( ( | 264 | if( ( |
245 | (om.has_field("claimed_id")?1:0) | 265 | (om.has_field("claimed_id")?1:0) |
246 | ^ | 266 | ^ |
247 | (om.has_field("identity")?1:0) | 267 | (om.has_field("identity")?1:0) |
248 | )&1 ) | 268 | )&1 ) |
249 | throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent"); | 269 | throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent"); |
250 | 270 | ||
251 | string turl = util::rfc_3986_normalize_uri(get_this_url()); | 271 | string turl = util::rfc_3986_normalize_uri(get_this_url()); |
252 | util::strip_uri_fragment_part(turl); | 272 | util::strip_uri_fragment_part(turl); |
253 | string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to")); | 273 | string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to")); |
254 | util::strip_uri_fragment_part(rurl); | 274 | util::strip_uri_fragment_part(rurl); |
255 | string::size_type | 275 | string::size_type |
256 | tq = turl.find('?'), rq = rurl.find('?'); | 276 | tq = turl.find('?'), rq = rurl.find('?'); |
257 | if( | 277 | if( |
258 | ((tq==string::npos)?turl:turl.substr(0,tq)) | 278 | ((tq==string::npos)?turl:turl.substr(0,tq)) |
259 | != | 279 | != |
260 | ((rq==string::npos)?rurl:rurl.substr(0,rq)) | 280 | ((rq==string::npos)?rurl:rurl.substr(0,rq)) |
261 | ) | 281 | ) |
262 | throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url"); | 282 | throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url"); |
263 | map<string,string> tp; parse_query(turl,tq,tp); | 283 | map<string,string> tp; parse_query(turl,tq,tp); |
264 | map<string,string> rp; parse_query(rurl,rq,rp); | 284 | map<string,string> rp; parse_query(rurl,rq,rp); |
265 | for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) { | 285 | for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) { |
266 | map<string,string>::const_iterator tpi = tp.find(rpi->first); | 286 | map<string,string>::const_iterator tpi = tp.find(rpi->first); |
267 | if(tpi==tp.end()) | 287 | if(tpi==tp.end()) |
268 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request"); | 288 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request"); |
269 | if(tpi->second!=rpi->second) | 289 | if(tpi->second!=rpi->second) |
270 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request"); | 290 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request"); |
271 | } | 291 | } |
272 | 292 | ||
273 | if(om.has_field("claimed_id")) { | 293 | if(om.has_field("claimed_id")) { |
294 | claimed_id = om.get_field("claimed_id"); | ||
295 | identity = om.get_field("identity"); | ||
274 | verify_OP( | 296 | verify_OP( |
275 | om.get_field("op_endpoint"), | 297 | om.get_field("op_endpoint"), |
276 | om.get_field("claimed_id"), | 298 | claimed_id, identity ); |
277 | om.get_field("identity") ); | ||
278 | } | 299 | } |
279 | 300 | ||
301 | }else{ | ||
302 | claimed_id = get_endpoint().claimed_id; | ||
303 | /* TODO: check if this is the identity we asked for */ | ||
304 | identity = om.get_field("identity"); | ||
280 | } | 305 | } |
281 | if(ext) ext->rp_id_res_hook(om,signeds); | 306 | if(ext) ext->rp_id_res_hook(om,signeds); |
282 | } | 307 | } |
283 | 308 | ||
284 | void basic_RP::check_authentication(const string& OP, | 309 | void basic_RP::check_authentication(const string& OP, |
285 | const basic_openid_message& om){ | 310 | const basic_openid_message& om){ |
286 | openid_message_t res; | 311 | openid_message_t res; |
287 | static const string checkauthmode = "check_authentication"; | 312 | static const string checkauthmode = "check_authentication"; |
288 | direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP); | 313 | direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP); |
289 | if(res.has_field("is_valid")) { | 314 | if(res.has_field("is_valid")) { |
290 | if(res.get_field("is_valid")=="true") { | 315 | if(res.get_field("is_valid")=="true") { |
291 | if(res.has_field("invalidate_handle")) | 316 | if(res.has_field("invalidate_handle")) |
292 | invalidate_assoc(OP,res.get_field("invalidate_handle")); | 317 | invalidate_assoc(OP,res.get_field("invalidate_handle")); |
293 | return; | 318 | return; |
294 | } | 319 | } |
295 | } | 320 | } |
296 | throw failed_check_authentication( | 321 | throw failed_check_authentication( |
297 | OPKELE_CP_ "failed to verify response"); | 322 | OPKELE_CP_ "failed to verify response"); |
298 | } | 323 | } |
299 | 324 | ||
300 | } | 325 | } |