-rw-r--r-- | include/opkele/exception.h | 9 | ||||
-rw-r--r-- | lib/basic_op.cc | 36 |
2 files changed, 45 insertions, 0 deletions
diff --git a/include/opkele/exception.h b/include/opkele/exception.h index 5c8418e..33f89cc 100644 --- a/include/opkele/exception.h +++ b/include/opkele/exception.h | |||
@@ -312,51 +312,60 @@ namespace opkele { | |||
312 | }; | 312 | }; |
313 | 313 | ||
314 | /** | 314 | /** |
315 | * thrown by associations store related functions in case of dumb RP. | 315 | * thrown by associations store related functions in case of dumb RP. |
316 | */ | 316 | */ |
317 | class dumb_RP : public exception { | 317 | class dumb_RP : public exception { |
318 | public: | 318 | public: |
319 | dumb_RP(OPKELE_E_PARS) | 319 | dumb_RP(OPKELE_E_PARS) |
320 | : exception(OPKELE_E_CONS) { } | 320 | : exception(OPKELE_E_CONS) { } |
321 | }; | 321 | }; |
322 | 322 | ||
323 | /** | 323 | /** |
324 | * thrown by endpoint-queue related function if endpoint is being | 324 | * thrown by endpoint-queue related function if endpoint is being |
325 | * accessed but there's no endpoint available. | 325 | * accessed but there's no endpoint available. |
326 | */ | 326 | */ |
327 | class no_endpoint : public exception { | 327 | class no_endpoint : public exception { |
328 | public: | 328 | public: |
329 | no_endpoint(OPKELE_E_PARS) | 329 | no_endpoint(OPKELE_E_PARS) |
330 | : exception(OPKELE_E_CONS) { } | 330 | : exception(OPKELE_E_CONS) { } |
331 | }; | 331 | }; |
332 | 332 | ||
333 | /** | 333 | /** |
334 | * thrown while processing OpenID request in OP. Signifies invalid realm | 334 | * thrown while processing OpenID request in OP. Signifies invalid realm |
335 | */ | 335 | */ |
336 | class bad_realm : public exception { | 336 | class bad_realm : public exception { |
337 | public: | 337 | public: |
338 | bad_realm(OPKELE_E_PARS) | 338 | bad_realm(OPKELE_E_PARS) |
339 | : exception(OPKELE_E_CONS) { } | 339 | : exception(OPKELE_E_CONS) { } |
340 | }; | 340 | }; |
341 | 341 | ||
342 | /** | 342 | /** |
343 | * thrown when attempting to retrieve return_to of one-way request | 343 | * thrown when attempting to retrieve return_to of one-way request |
344 | */ | 344 | */ |
345 | class no_return_to : public exception { | 345 | class no_return_to : public exception { |
346 | public: | 346 | public: |
347 | no_return_to(OPKELE_E_PARS) | 347 | no_return_to(OPKELE_E_PARS) |
348 | : exception(OPKELE_E_CONS) { } | 348 | : exception(OPKELE_E_CONS) { } |
349 | }; | 349 | }; |
350 | 350 | ||
351 | /** | 351 | /** |
352 | * thrown when querying identity of non-identity related request | 352 | * thrown when querying identity of non-identity related request |
353 | */ | 353 | */ |
354 | class non_identity : public exception { | 354 | class non_identity : public exception { |
355 | public: | 355 | public: |
356 | non_identity(OPKELE_E_PARS) | 356 | non_identity(OPKELE_E_PARS) |
357 | : exception(OPKELE_E_CONS) { } | 357 | : exception(OPKELE_E_CONS) { } |
358 | }; | 358 | }; |
359 | 359 | ||
360 | /** | ||
361 | * thrown if return_to URL doesn't match realm | ||
362 | */ | ||
363 | class bad_return_to : public exception { | ||
364 | public: | ||
365 | bad_return_to(OPKELE_E_PARS) | ||
366 | : exception(OPKELE_E_CONS) { } | ||
367 | }; | ||
368 | |||
360 | } | 369 | } |
361 | 370 | ||
362 | #endif /* __OPKELE_EXCEPTION_H */ | 371 | #endif /* __OPKELE_EXCEPTION_H */ |
diff --git a/lib/basic_op.cc b/lib/basic_op.cc index 22012bc..f7573aa 100644 --- a/lib/basic_op.cc +++ b/lib/basic_op.cc | |||
@@ -1,59 +1,62 @@ | |||
1 | #include <time.h> | 1 | #include <time.h> |
2 | #include <cassert> | 2 | #include <cassert> |
3 | #include <algorithm> | ||
3 | #include <openssl/sha.h> | 4 | #include <openssl/sha.h> |
4 | #include <openssl/hmac.h> | 5 | #include <openssl/hmac.h> |
5 | #include <opkele/data.h> | 6 | #include <opkele/data.h> |
6 | #include <opkele/basic_op.h> | 7 | #include <opkele/basic_op.h> |
7 | #include <opkele/exception.h> | 8 | #include <opkele/exception.h> |
8 | #include <opkele/util.h> | 9 | #include <opkele/util.h> |
9 | #include <opkele/uris.h> | 10 | #include <opkele/uris.h> |
10 | 11 | ||
11 | namespace opkele { | 12 | namespace opkele { |
13 | using std::pair; | ||
14 | using std::mismatch; | ||
12 | 15 | ||
13 | void basic_op::reset_vars() { | 16 | void basic_op::reset_vars() { |
14 | assoc.reset(); | 17 | assoc.reset(); |
15 | return_to.clear(); realm.clear(); | 18 | return_to.clear(); realm.clear(); |
16 | claimed_id.clear(); identity.clear(); | 19 | claimed_id.clear(); identity.clear(); |
17 | invalidate_handle.clear(); | 20 | invalidate_handle.clear(); |
18 | } | 21 | } |
19 | 22 | ||
20 | bool basic_op::has_return_to() const { | 23 | bool basic_op::has_return_to() const { |
21 | return !return_to.empty(); | 24 | return !return_to.empty(); |
22 | } | 25 | } |
23 | const string& basic_op::get_return_to() const { | 26 | const string& basic_op::get_return_to() const { |
24 | if(return_to.empty()) | 27 | if(return_to.empty()) |
25 | throw no_return_to(OPKELE_CP_ "No return_to URL provided with request"); | 28 | throw no_return_to(OPKELE_CP_ "No return_to URL provided with request"); |
26 | return return_to; | 29 | return return_to; |
27 | } | 30 | } |
28 | 31 | ||
29 | const string& basic_op::get_realm() const { | 32 | const string& basic_op::get_realm() const { |
30 | assert(!realm.empty()); | 33 | assert(!realm.empty()); |
31 | return realm; | 34 | return realm; |
32 | } | 35 | } |
33 | 36 | ||
34 | bool basic_op::has_identity() const { | 37 | bool basic_op::has_identity() const { |
35 | return !identity.empty(); | 38 | return !identity.empty(); |
36 | } | 39 | } |
37 | const string& basic_op::get_claimed_id() const { | 40 | const string& basic_op::get_claimed_id() const { |
38 | if(claimed_id.empty()) | 41 | if(claimed_id.empty()) |
39 | throw non_identity(OPKELE_CP_ "attempting to retrieve claimed_id of non-identity related request"); | 42 | throw non_identity(OPKELE_CP_ "attempting to retrieve claimed_id of non-identity related request"); |
40 | assert(!identity.empty()); | 43 | assert(!identity.empty()); |
41 | return claimed_id; | 44 | return claimed_id; |
42 | } | 45 | } |
43 | const string& basic_op::get_identity() const { | 46 | const string& basic_op::get_identity() const { |
44 | if(identity.empty()) | 47 | if(identity.empty()) |
45 | throw non_identity(OPKELE_CP_ "attempting to retrieve identity of non-identity related request"); | 48 | throw non_identity(OPKELE_CP_ "attempting to retrieve identity of non-identity related request"); |
46 | assert(!claimed_id.empty()); | 49 | assert(!claimed_id.empty()); |
47 | return identity; | 50 | return identity; |
48 | } | 51 | } |
49 | 52 | ||
50 | bool basic_op::is_id_select() const { | 53 | bool basic_op::is_id_select() const { |
51 | return identity==IDURI_SELECT20; | 54 | return identity==IDURI_SELECT20; |
52 | } | 55 | } |
53 | 56 | ||
54 | void basic_op::select_identity(const string& c,const string& i) { | 57 | void basic_op::select_identity(const string& c,const string& i) { |
55 | claimed_id = c; identity = i; | 58 | claimed_id = c; identity = i; |
56 | } | 59 | } |
57 | void basic_op::set_claimed_id(const string& c) { | 60 | void basic_op::set_claimed_id(const string& c) { |
58 | claimed_id = c; | 61 | claimed_id = c; |
59 | } | 62 | } |
@@ -272,49 +275,82 @@ namespace opkele { | |||
272 | bool o2; | 275 | bool o2; |
273 | try { | 276 | try { |
274 | o2 = (inm.get_field("ns")==OIURI_OPENID20); | 277 | o2 = (inm.get_field("ns")==OIURI_OPENID20); |
275 | }catch(failed_lookup&) { o2 = false; } | 278 | }catch(failed_lookup&) { o2 = false; } |
276 | string nonce; | 279 | string nonce; |
277 | if(o2) { | 280 | if(o2) { |
278 | try { | 281 | try { |
279 | if(!check_nonce(nonce = inm.get_field("response_nonce"))) | 282 | if(!check_nonce(nonce = inm.get_field("response_nonce"))) |
280 | throw failed_check_authentication(OPKELE_CP_ "Invalid nonce"); | 283 | throw failed_check_authentication(OPKELE_CP_ "Invalid nonce"); |
281 | }catch(failed_lookup&) { | 284 | }catch(failed_lookup&) { |
282 | throw failed_check_authentication(OPKELE_CP_ "No nonce provided with check_authentication request"); | 285 | throw failed_check_authentication(OPKELE_CP_ "No nonce provided with check_authentication request"); |
283 | } | 286 | } |
284 | } | 287 | } |
285 | try { | 288 | try { |
286 | assoc = retrieve_assoc(inm.get_field("assoc_handle")); | 289 | assoc = retrieve_assoc(inm.get_field("assoc_handle")); |
287 | if(!assoc->stateless()) | 290 | if(!assoc->stateless()) |
288 | throw failed_check_authentication(OPKELE_CP_ "Will not do check_authentication on a stateful handle"); | 291 | throw failed_check_authentication(OPKELE_CP_ "Will not do check_authentication on a stateful handle"); |
289 | }catch(failed_lookup&) { | 292 | }catch(failed_lookup&) { |
290 | throw failed_check_authentication(OPKELE_CP_ "No assoc_handle or invalid assoc_handle specified with check_authentication request"); | 293 | throw failed_check_authentication(OPKELE_CP_ "No assoc_handle or invalid assoc_handle specified with check_authentication request"); |
291 | } | 294 | } |
292 | static const string idresmode = "id_res"; | 295 | static const string idresmode = "id_res"; |
293 | try { | 296 | try { |
294 | if(util::base64_signature(assoc,util::change_mode_message_proxy(inm,idresmode))!=inm.get_field("sig")) | 297 | if(util::base64_signature(assoc,util::change_mode_message_proxy(inm,idresmode))!=inm.get_field("sig")) |
295 | throw failed_check_authentication(OPKELE_CP_ "Signature mismatch"); | 298 | throw failed_check_authentication(OPKELE_CP_ "Signature mismatch"); |
296 | }catch(failed_lookup&) { | 299 | }catch(failed_lookup&) { |
297 | throw failed_check_authentication(OPKELE_CP_ "failed to calculate signature"); | 300 | throw failed_check_authentication(OPKELE_CP_ "failed to calculate signature"); |
298 | } | 301 | } |
299 | oum.set_field("is_valid","true"); | 302 | oum.set_field("is_valid","true"); |
300 | try { | 303 | try { |
301 | string h = inm.get_field("invalidate_handle"); | 304 | string h = inm.get_field("invalidate_handle"); |
302 | try { | 305 | try { |
303 | assoc_t ih = retrieve_assoc(h); | 306 | assoc_t ih = retrieve_assoc(h); |
304 | }catch(invalid_handle& ih) { | 307 | }catch(invalid_handle& ih) { |
305 | oum.set_field("invalidate_handle",h); | 308 | oum.set_field("invalidate_handle",h); |
306 | }catch(failed_lookup& ih) { | 309 | }catch(failed_lookup& ih) { |
307 | oum.set_field("invalidate_handle",h); | 310 | oum.set_field("invalidate_handle",h); |
308 | } | 311 | } |
309 | }catch(failed_lookup&) { } | 312 | }catch(failed_lookup&) { } |
310 | if(o2) { | 313 | if(o2) { |
311 | assert(!nonce.empty()); | 314 | assert(!nonce.empty()); |
312 | invalidate_nonce(nonce); | 315 | invalidate_nonce(nonce); |
313 | } | 316 | } |
314 | return oum; | 317 | return oum; |
315 | }catch(failed_check_authentication& ) { | 318 | }catch(failed_check_authentication& ) { |
316 | oum.set_field("is_valid","false"); | 319 | oum.set_field("is_valid","false"); |
317 | return oum; | 320 | return oum; |
318 | } | 321 | } |
319 | 322 | ||
323 | void basic_op::verify_return_to() { | ||
324 | string nrealm = opkele::util::rfc_3986_normalize_uri(realm); | ||
325 | if(nrealm.find('#')!=string::npos) | ||
326 | throw opkele::bad_realm(OPKELE_CP_ "authentication realm contains URI fragment"); | ||
327 | string nrt = opkele::util::rfc_3986_normalize_uri(return_to); | ||
328 | string::size_type pr = nrealm.find("://"); | ||
329 | string::size_type prt = nrt.find("://"); | ||
330 | assert(!(pr==string::npos || prt==string::npos)); | ||
331 | pr += sizeof("://")-1; | ||
332 | prt += sizeof("://")-1; | ||
333 | if(!strncmp(nrealm.c_str()+pr,"*.",2)) { | ||
334 | pr = nrealm.find('.',pr); | ||
335 | prt = nrt.find('.',prt); | ||
336 | assert(pr!=string::npos); | ||
337 | if(prt==string::npos) | ||
338 | throw bad_return_to( | ||
339 | OPKELE_CP_ "return_to URL doesn't match realm"); | ||
340 | // TODO: check for overgeneralized realm | ||
341 | } | ||
342 | string::size_type lr = nrealm.length(); | ||
343 | string::size_type lrt = nrt.length(); | ||
344 | if( (lrt-prt) < (lr-pr) ) | ||
345 | throw bad_return_to( | ||
346 | OPKELE_CP_ "return_to URL doesn't match realm"); | ||
347 | pair<const char*,const char*> mp = mismatch( | ||
348 | nrealm.c_str()+pr,nrealm.c_str()+lr, | ||
349 | nrt.c_str()+prt); | ||
350 | if( (*(mp.first-1))!='/' | ||
351 | && !strchr("/?#",*mp.second) ) | ||
352 | throw bad_return_to( | ||
353 | OPKELE_CP_ "return_to URL doesn't match realm"); | ||
354 | } | ||
355 | |||
320 | } | 356 | } |