-rw-r--r-- | lib/basic_op.cc | 29 | ||||
-rw-r--r-- | lib/basic_rp.cc | 10 | ||||
-rw-r--r-- | lib/discovery.cc | 2 | ||||
-rw-r--r-- | lib/expat.cc | 1 | ||||
-rw-r--r-- | lib/extension.cc | 2 | ||||
-rw-r--r-- | lib/openid_message.cc | 32 | ||||
-rw-r--r-- | lib/prequeue_rp.cc | 4 | ||||
-rw-r--r-- | lib/sreg.cc | 6 | ||||
-rw-r--r-- | lib/verify_op.cc | 4 |
9 files changed, 44 insertions, 46 deletions
diff --git a/lib/basic_op.cc b/lib/basic_op.cc index 18446dc..2d82147 100644 --- a/lib/basic_op.cc +++ b/lib/basic_op.cc @@ -1,330 +1,327 @@ #include <time.h> #include <cassert> #include <openssl/sha.h> #include <openssl/hmac.h> #include <opkele/data.h> #include <opkele/basic_op.h> #include <opkele/exception.h> #include <opkele/util.h> #include <opkele/uris.h> namespace opkele { void basic_OP::reset_vars() { assoc.reset(); return_to.clear(); realm.clear(); claimed_id.clear(); identity.clear(); invalidate_handle.clear(); } bool basic_OP::has_return_to() const { return !return_to.empty(); } const string& basic_OP::get_return_to() const { if(return_to.empty()) throw no_return_to(OPKELE_CP_ "No return_to URL provided with request"); return return_to; } const string& basic_OP::get_realm() const { assert(!realm.empty()); return realm; } bool basic_OP::has_identity() const { return !identity.empty(); } const string& basic_OP::get_claimed_id() const { if(claimed_id.empty()) throw non_identity(OPKELE_CP_ "attempting to retrieve claimed_id of non-identity related request"); assert(!identity.empty()); return claimed_id; } const string& basic_OP::get_identity() const { if(identity.empty()) throw non_identity(OPKELE_CP_ "attempting to retrieve identity of non-identity related request"); assert(!claimed_id.empty()); return identity; } bool basic_OP::is_id_select() const { return identity==IDURI_SELECT20; } void basic_OP::select_identity(const string& c,const string& i) { claimed_id = c; identity = i; } void basic_OP::set_claimed_id(const string& c) { claimed_id = c; } basic_openid_message& basic_OP::associate( basic_openid_message& oum, const basic_openid_message& inm) try { assert(inm.get_field("mode")=="associate"); util::dh_t dh; util::bignum_t c_pub; unsigned char key_digest[SHA256_DIGEST_LENGTH]; size_t d_len = 0; - enum { - sess_cleartext, sess_dh_sha1, sess_dh_sha256 - } st = sess_cleartext; string sts = inm.get_field("session_type"); string ats = inm.get_field("assoc_type"); if(sts=="DH-SHA1" || sts=="DH-SHA256") { if(!(dh = DH_new())) throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); c_pub = util::base64_to_bignum(inm.get_field("dh_consumer_public")); try { dh->p = util::base64_to_bignum(inm.get_field("dh_modulus")); }catch(failed_lookup&) { dh->p = util::dec_to_bignum(data::_default_p); } try { dh->g = util::base64_to_bignum(inm.get_field("dh_gen")); }catch(failed_lookup&) { dh->g = util::dec_to_bignum(data::_default_g); } if(!DH_generate_key(dh)) throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); vector<unsigned char> ck(DH_size(dh)+1); unsigned char *ckptr = &(ck.front())+1; int cklen = DH_compute_key(ckptr,c_pub,dh); if(cklen<0) throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); if(cklen && (*ckptr)&0x80) { (*(--ckptr)) = 0; ++cklen; } if(sts=="DH-SHA1") { SHA1(ckptr,cklen,key_digest); d_len = SHA_DIGEST_LENGTH; }else if(sts=="DH-SHA256") { SHA256(ckptr,cklen,key_digest); d_len = SHA256_DIGEST_LENGTH; }else throw internal_error(OPKELE_CP_ "I thought I knew the session type"); }else throw unsupported(OPKELE_CP_ "Unsupported session_type"); - assoc_t assoc; + assoc_t a; if(ats=="HMAC-SHA1") - assoc = alloc_assoc(ats,SHA_DIGEST_LENGTH,true); + a = alloc_assoc(ats,SHA_DIGEST_LENGTH,true); else if(ats=="HMAC-SHA256") - assoc = alloc_assoc(ats,SHA256_DIGEST_LENGTH,true); + a = alloc_assoc(ats,SHA256_DIGEST_LENGTH,true); else throw unsupported(OPKELE_CP_ "Unsupported assoc_type"); oum.reset_fields(); oum.set_field("ns",OIURI_OPENID20); - oum.set_field("assoc_type",assoc->assoc_type()); - oum.set_field("assoc_handle",assoc->handle()); + oum.set_field("assoc_type",a->assoc_type()); + oum.set_field("assoc_handle",a->handle()); oum.set_field("expires_in",util::long_to_string(assoc->expires_in())); - secret_t secret = assoc->secret(); + secret_t secret = a->secret(); if(sts=="DH-SHA1" || sts=="DH-SHA256") { if(d_len != secret.size()) throw bad_input(OPKELE_CP_ "Association secret and session MAC are not of the same size"); oum.set_field("session_type",sts); oum.set_field("dh_server_public",util::bignum_to_base64(dh->pub_key)); string b64; secret.enxor_to_base64(key_digest,b64); oum.set_field("enc_mac_key",b64); }else /* TODO: support cleartext over encrypted connection */ throw unsupported(OPKELE_CP_ "Unsupported session type"); return oum; } catch(unsupported& u) { oum.reset_fields(); oum.set_field("ns",OIURI_OPENID20); oum.set_field("error",u.what()); oum.set_field("error_code","unsupported-type"); oum.set_field("session_type","DH-SHA256"); oum.set_field("assoc_type","HMAC-SHA256"); return oum; } void basic_OP::checkid_(const basic_openid_message& inm, extension_t *ext) { reset_vars(); - string mode = inm.get_field("mode"); - if(mode=="checkid_setup") + string modestr = inm.get_field("mode"); + if(modestr=="checkid_setup") mode = mode_checkid_setup; - else if(mode=="checkid_immediate") + else if(modestr=="checkid_immediate") mode = mode_checkid_immediate; else throw bad_input(OPKELE_CP_ "Invalid checkid_* mode"); try { assoc = retrieve_assoc(invalidate_handle=inm.get_field("assoc_handle")); invalidate_handle.clear(); }catch(failed_lookup&) { } try { openid2 = (inm.get_field("ns")==OIURI_OPENID20); }catch(failed_lookup&) { openid2 = false; } try { return_to = inm.get_field("return_to"); }catch(failed_lookup&) { } if(openid2) { try { realm = inm.get_field("realm"); }catch(failed_lookup&) { try { realm = inm.get_field("trust_root"); }catch(failed_lookup&) { if(return_to.empty()) throw bad_input(OPKELE_CP_ "Both realm and return_to are unset"); realm = return_to; } } }else{ try { realm = inm.get_field("trust_root"); }catch(failed_lookup&) { if(return_to.empty()) throw bad_input(OPKELE_CP_ "Both realm and return_to are unset"); realm = return_to; } } try { identity = inm.get_field("identity"); try { claimed_id = inm.get_field("claimed_id"); }catch(failed_lookup&) { if(openid2) throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent"); claimed_id = identity; } }catch(failed_lookup&) { if(openid2 && inm.has_field("claimed_id")) throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent"); } verify_return_to(); if(ext) ext->op_checkid_hook(inm); } basic_openid_message& basic_OP::id_res(basic_openid_message& om, extension_t *ext) { assert(!return_to.empty()); assert(!is_id_select()); if(!assoc) { assoc = alloc_assoc("HMAC-SHA256",SHA256_DIGEST_LENGTH,true); } time_t now = time(0); struct tm gmt; gmtime_r(&now,&gmt); char w3timestr[24]; if(!strftime(w3timestr,sizeof(w3timestr),"%Y-%m-%dT%H:%M:%SZ",&gmt)) throw failed_conversion(OPKELE_CP_ "Failed to build time string for nonce" ); om.set_field("ns",OIURI_OPENID20); om.set_field("mode","id_res"); om.set_field("op_endpoint",get_op_endpoint()); string ats = "ns,mode,op_endpoint,return_to,response_nonce," "assoc_handle,signed"; if(!identity.empty()) { om.set_field("identity",identity); om.set_field("claimed_id",claimed_id); ats += ",identity,claimed_id"; } om.set_field("return_to",return_to); string nonce = w3timestr; om.set_field("response_nonce",alloc_nonce(nonce)); if(!invalidate_handle.empty()) { om.set_field("invalidate_handle",invalidate_handle); ats += ",invalidate_handle"; } om.set_field("assoc_handle",assoc->handle()); om.add_to_signed(ats); if(ext) ext->op_id_res_hook(om); om.set_field("sig",util::base64_signature(assoc,om)); return om; } basic_openid_message& basic_OP::cancel(basic_openid_message& om) { assert(!return_to.empty()); om.set_field("ns",OIURI_OPENID20); om.set_field("mode","cancel"); return om; } basic_openid_message& basic_OP::error(basic_openid_message& om, - const string& error,const string& contact, + const string& err,const string& contact, const string& reference ) { assert(!return_to.empty()); om.set_field("ns",OIURI_OPENID20); om.set_field("mode","error"); - om.set_field("error",error); - om.set_field("contact",contact); - om.set_field("reference",reference); + om.set_field("error",err); + if(!contact.empty()) om.set_field("contact",contact); + if(!reference.empty()) om.set_field("reference",reference); return om; } basic_openid_message& basic_OP::setup_needed( basic_openid_message& oum,const basic_openid_message& inm) { assert(mode==mode_checkid_immediate); assert(!return_to.empty()); if(openid2) { oum.set_field("ns",OIURI_OPENID20); oum.set_field("mode","setup_needed"); }else{ oum.set_field("mode","id_res"); static const string setupmode = "checkid_setup"; oum.set_field("user_setup_url", util::change_mode_message_proxy(inm,setupmode) .append_query(get_op_endpoint())); } return oum; } basic_openid_message& basic_OP::check_authentication( basic_openid_message& oum, const basic_openid_message& inm) try { assert(inm.get_field("mode")=="check_authentication"); oum.reset_fields(); oum.set_field("ns",OIURI_OPENID20); bool o2; try { o2 = (inm.get_field("ns")==OIURI_OPENID20); }catch(failed_lookup&) { o2 = false; } string nonce; if(o2) { try { if(!check_nonce(nonce = inm.get_field("response_nonce"))) throw failed_check_authentication(OPKELE_CP_ "Invalid nonce"); }catch(failed_lookup&) { throw failed_check_authentication(OPKELE_CP_ "No nonce provided with check_authentication request"); } } try { assoc = retrieve_assoc(inm.get_field("assoc_handle")); if(!assoc->stateless()) throw failed_check_authentication(OPKELE_CP_ "Will not do check_authentication on a stateful handle"); }catch(failed_lookup&) { throw failed_check_authentication(OPKELE_CP_ "No assoc_handle or invalid assoc_handle specified with check_authentication request"); } static const string idresmode = "id_res"; try { if(util::base64_signature(assoc,util::change_mode_message_proxy(inm,idresmode))!=inm.get_field("sig")) throw failed_check_authentication(OPKELE_CP_ "Signature mismatch"); }catch(failed_lookup&) { throw failed_check_authentication(OPKELE_CP_ "failed to calculate signature"); } oum.set_field("is_valid","true"); try { string h = inm.get_field("invalidate_handle"); try { assoc_t ih = retrieve_assoc(h); }catch(invalid_handle& ih) { oum.set_field("invalidate_handle",h); }catch(failed_lookup& ih) { oum.set_field("invalidate_handle",h); } }catch(failed_lookup&) { } if(o2) { assert(!nonce.empty()); invalidate_nonce(nonce); } return oum; }catch(failed_check_authentication& ) { oum.set_field("is_valid","false"); return oum; } void basic_OP::verify_return_to() { if(realm.find('#')!=string::npos) throw opkele::bad_realm(OPKELE_CP_ "authentication realm contains URI fragment"); if(!util::uri_matches_realm(return_to,realm)) throw bad_return_to(OPKELE_CP_ "return_to URL doesn't match realm"); } } diff --git a/lib/basic_rp.cc b/lib/basic_rp.cc index bd45d99..a0ad130 100644 --- a/lib/basic_rp.cc +++ b/lib/basic_rp.cc @@ -1,297 +1,299 @@ +#include <cassert> #include <openssl/sha.h> #include <openssl/hmac.h> #include <opkele/basic_rp.h> #include <opkele/exception.h> #include <opkele/uris.h> #include <opkele/data.h> #include <opkele/util.h> #include <opkele/curl.h> namespace opkele { static void dh_get_secret( secret_t& secret, const basic_openid_message& om, const char *exp_assoc, const char *exp_sess, util::dh_t& dh, size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*), size_t exp_s_len) try { if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess) throw bad_input(OPKELE_CP_ "Unexpected associate response"); util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public")); vector<unsigned char> ck(DH_size(dh)+1); unsigned char *ckptr = &(ck.front())+1; int cklen = DH_compute_key(ckptr,s_pub,dh); if(cklen<0) throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); if(cklen && (*ckptr)&0x80) { (*(--ckptr))=0; ++cklen; } - unsigned char key_digest[d_len]; + assert(d_len<=SHA256_DIGEST_LENGTH); + unsigned char key_digest[SHA256_DIGEST_LENGTH]; secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key")); if(secret.size()!=exp_s_len) throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type"); }catch(opkele::failed_lookup& ofl) { throw bad_input(OPKELE_CP_ "Incoherent response from OP"); } OPKELE_RETHROW static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) { util::curl_pick_t curl = util::curl_pick_t::easy_init(); if(!curl) throw exception_curl(OPKELE_CP_ "failed to initialize curl"); string request = inm.query_string(); CURLcode r; (r=curl.misc_sets()) || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str())) || (r=curl.easy_setopt(CURLOPT_POST,1)) || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) || (r=curl.set_write()); if(r) throw exception_curl(OPKELE_CP_ "failed to set curly options",r); if( (r=curl.easy_perform()) ) throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); oum.from_keyvalues(curl.response); } assoc_t basic_RP::associate(const string& OP) { util::dh_t dh = DH_new(); if(!dh) throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); dh->p = util::dec_to_bignum(data::_default_p); dh->g = util::dec_to_bignum(data::_default_g); if(!DH_generate_key(dh)) throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); openid_message_t req; req.set_field("ns",OIURI_OPENID20); req.set_field("mode","associate"); req.set_field("dh_modulus",util::bignum_to_base64(dh->p)); req.set_field("dh_gen",util::bignum_to_base64(dh->g)); req.set_field("dh_consumer_public",util::bignum_to_base64(dh->pub_key)); openid_message_t res; req.set_field("assoc_type","HMAC-SHA256"); req.set_field("session_type","DH-SHA256"); secret_t secret; int expires_in; try { direct_request(res,req,OP); dh_get_secret( secret, res, "HMAC-SHA256", "DH-SHA256", dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH ); expires_in = util::string_to_long(res.get_field("expires_in")); - }catch(exception& e) { + }catch(exception&) { try { req.set_field("assoc_type","HMAC-SHA1"); req.set_field("session_type","DH-SHA1"); direct_request(res,req,OP); dh_get_secret( secret, res, "HMAC-SHA1", "DH-SHA1", dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH ); expires_in = util::string_to_long(res.get_field("expires_in")); - }catch(bad_input& e) { + }catch(bad_input&) { throw dumb_RP(OPKELE_CP_ "OP failed to supply an association"); } } return store_assoc( OP, res.get_field("assoc_handle"), res.get_field("assoc_type"), secret, expires_in ); } basic_openid_message& basic_RP::checkid_( basic_openid_message& rv, mode_t mode, const string& return_to,const string& realm, extension_t *ext) { rv.reset_fields(); rv.set_field("ns",OIURI_OPENID20); if(mode==mode_checkid_immediate) rv.set_field("mode","checkid_immediate"); else if(mode==mode_checkid_setup) rv.set_field("mode","checkid_setup"); else throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); if(realm.empty() && return_to.empty()) throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty"); if(!realm.empty()) { rv.set_field("realm",realm); rv.set_field("trust_root",realm); } if(!return_to.empty()) rv.set_field("return_to",return_to); const openid_endpoint_t& ep = get_endpoint(); rv.set_field("claimed_id",ep.claimed_id); rv.set_field("identity",ep.local_id); try { rv.set_field("assoc_handle",find_assoc(ep.uri)->handle()); }catch(dumb_RP& drp) { }catch(failed_lookup& fl) { try { rv.set_field("assoc_handle",associate(ep.uri)->handle()); }catch(dumb_RP& drp) { } } OPKELE_RETHROW if(ext) ext->rp_checkid_hook(rv); return rv; } class signed_part_message_proxy : public basic_openid_message { public: const basic_openid_message& x; set<string> signeds; signed_part_message_proxy(const basic_openid_message& xx) : x(xx) { const string& slist = x.get_field("signed"); string::size_type p = 0; while(true) { string::size_type co = slist.find(',',p); string f = (co==string::npos) ?slist.substr(p):slist.substr(p,co-p); signeds.insert(f); if(co==string::npos) break; p = co+1; } } bool has_field(const string& n) const { return signeds.find(n)!=signeds.end() && x.has_field(n); } const string& get_field(const string& n) const { if(signeds.find(n)==signeds.end()) throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed"); return x.get_field(n); } fields_iterator fields_begin() const { return signeds.begin(); } fields_iterator fields_end() const { return signeds.end(); } }; static void parse_query(const string& u,string::size_type q, map<string,string>& p) { if(q==string::npos) return; assert(u[q]=='?'); ++q; string::size_type l = u.size(); while(q<l) { string::size_type eq = u.find('=',q); string::size_type am = u.find('&',q); if(am==string::npos) { if(eq==string::npos) { p[""] = u.substr(q); }else{ p[u.substr(q,eq-q)] = u.substr(eq+1); } break; }else{ if(eq==string::npos || eq>am) { p[""] = u.substr(q,eq-q); }else{ p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1); } q = ++am; } } } void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) { bool o2 = om.has_field("ns") && om.get_field("ns")==OIURI_OPENID20; if( (!o2) && om.has_field("user_setup_url")) throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided", om.get_field("user_setup_url")); string m = om.get_field("mode"); if(o2 && m=="setup_needed") throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided"); if(m=="cancel") throw id_res_cancel(OPKELE_CP_ "authentication cancelled"); bool go_dumb=false; try { string OP = o2 ?om.get_field("op_endpoint") :get_endpoint().uri; assoc_t assoc = retrieve_assoc( OP,om.get_field("assoc_handle")); if(om.get_field("sig")!=util::base64_signature(assoc,om)) throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); }catch(dumb_RP& drp) { go_dumb=true; }catch(failed_lookup& e) { go_dumb=true; } OPKELE_RETHROW if(go_dumb) { try { string OP = o2 ?om.get_field("op_endpoint") :get_endpoint().uri; check_authentication(OP,om); }catch(failed_check_authentication& fca) { throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); } OPKELE_RETHROW } signed_part_message_proxy signeds(om); if(o2) { check_nonce(om.get_field("op_endpoint"), om.get_field("response_nonce")); static const char *mustsign[] = { "op_endpoint", "return_to", "response_nonce", "assoc_handle", "claimed_id", "identity" }; - for(int ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) { + for(size_t ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) { if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms])) throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs"); } if( ( (om.has_field("claimed_id")?1:0) ^ (om.has_field("identity")?1:0) )&1 ) throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent"); string turl = util::rfc_3986_normalize_uri(get_this_url()); util::strip_uri_fragment_part(turl); string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to")); util::strip_uri_fragment_part(rurl); string::size_type tq = turl.find('?'), rq = rurl.find('?'); if( ((tq==string::npos)?turl:turl.substr(0,tq)) != ((rq==string::npos)?rurl:rurl.substr(0,rq)) ) throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url"); map<string,string> tp; parse_query(turl,tq,tp); map<string,string> rp; parse_query(rurl,rq,rp); for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) { map<string,string>::const_iterator tpi = tp.find(rpi->first); if(tpi==tp.end()) throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request"); if(tpi->second!=rpi->second) throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request"); } if(om.has_field("claimed_id")) { verify_OP( om.get_field("op_endpoint"), om.get_field("claimed_id"), om.get_field("identity") ); } } if(ext) ext->rp_id_res_hook(om,signeds); } void basic_RP::check_authentication(const string& OP, const basic_openid_message& om){ openid_message_t res; static const string checkauthmode = "check_authentication"; direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP); if(res.has_field("is_valid")) { if(res.get_field("is_valid")=="true") { if(res.has_field("invalidate_handle")) invalidate_assoc(OP,res.get_field("invalidate_handle")); return; } } throw failed_check_authentication( OPKELE_CP_ "failed to verify response"); } } diff --git a/lib/discovery.cc b/lib/discovery.cc index 6f58339..6f9926c 100644 --- a/lib/discovery.cc +++ b/lib/discovery.cc @@ -1,577 +1,577 @@ #include <list> #include <opkele/curl.h> #include <opkele/expat.h> #include <opkele/uris.h> #include <opkele/discovery.h> #include <opkele/exception.h> #include <opkele/util.h> #include <opkele/tidy.h> #include <opkele/debug.h> #include "config.h" #define XRDS_HEADER "X-XRDS-Location" #define CT_HEADER "Content-Type" namespace opkele { using std::list; using xrd::XRD_t; using xrd::service_t; /* TODO: the whole discovery thing needs cleanup and optimization due to * many changes of concept. */ static const char *whitespace = " \t\r\n"; static const char *i_leaders = "=@+$!("; static const size_t max_html = 16384; static const struct service_type_t { const char *uri; const char *forceid; } op_service_types[] = { { STURI_OPENID20_OP, IDURI_SELECT20 }, { STURI_OPENID20, 0 }, { STURI_OPENID11, 0 }, { STURI_OPENID10, 0 } }; enum { st_index_1 = 2, st_index_2 = 1 }; static inline bool is_qelement(const XML_Char *n,const char *qen) { return !strcasecmp(n,qen); } static inline bool is_element(const XML_Char *n,const char *en) { if(!strcasecmp(n,en)) return true; int nl = strlen(n), enl = strlen(en); if( (nl>=(enl+1)) && n[nl-enl-1]=='\t' && !strcasecmp(&n[nl-enl],en) ) return true; return false; } static long element_priority(const XML_Char **a) { for(;*a;++a) if(!strcasecmp(*(a++),"priority")) { long rv; return (sscanf(*a,"%ld",&rv)==1)?rv:-1; } return -1; } /* TODO: ideally all attributes should be * retrieved in one run */ static const char *element_attr(const XML_Char **a, const char *at) { for(;*a;++a) if(!strcasecmp(*(a++),at)) { return *a; } return 0; } class idigger_t : public util::curl_t, public util::expat_t { public: string xri_proxy; enum { xmode_html = 1, xmode_xrd = 2, xmode_cid = 4, xmode_noredirs = 8 }; int xmode; string xrds_location; string http_content_type; service_t html_openid1; service_t html_openid2; string cdata_buf; long status_code; string status_string; typedef list<string> pt_stack_t; pt_stack_t pt_stack; int skipping; bool parser_choked; string save_html; XRD_t *xrd; service_t *xrd_service; string* cdata; idigger_t() : util::curl_t(easy_init()), util::expat_t(0), xri_proxy(XRI_PROXY_URL) { CURLcode r; (r=misc_sets()) || (r=set_write()) || (r=set_header()) ; if(r) throw exception_curl(OPKELE_CP_ "failed to set curly options",r); } ~idigger_t() throw() { } void yadiscover(endpoint_discovery_iterator oi,const string& yurl,const char **types,bool redirs) { idiscovery_t idis; idis.xri_identity = false; discover_at(idis,yurl,xmode_html|xmode_xrd|(redirs?0:xmode_noredirs)); if(!xrds_location.empty()) { idis.clear(); discover_at(idis,xrds_location,xmode_xrd); } idis.normalized_id = idis.canonicalized_id = yurl; service_type_t st; for(st.uri=*types;*types;st.uri=*(++types)) queue_endpoints(oi,idis,&st); } string discover(endpoint_discovery_iterator& oi,const string& identity) { string rv; idiscovery_t idis; string::size_type fsc = identity.find_first_not_of(whitespace); if(fsc==string::npos) throw bad_input(OPKELE_CP_ "whitespace-only identity"); string::size_type lsc = identity.find_last_not_of(whitespace); assert(lsc!=string::npos); if(!strncasecmp(identity.c_str()+fsc,"xri://",sizeof("xri://")-1)) fsc += sizeof("xri://")-1; if((fsc+1)>=lsc) throw bad_input(OPKELE_CP_ "not a character of importance in identity"); string id(identity,fsc,lsc-fsc+1); idis.clear(); if(strchr(i_leaders,id[0])) { /* TODO: further normalize xri identity? Like folding case * or whatever... */ rv = id; set<string> cids; for(const struct service_type_t *st=op_service_types; st<&op_service_types[sizeof(op_service_types)/sizeof(*op_service_types)];++st) { idis.clear(); discover_at( idis, xri_proxy + util::url_encode(id)+ "?_xrd_t="+util::url_encode(st->uri)+ "&_xrd_r=application/xrd%2Bxml" ";sep=true;refs=true", xmode_xrd ); if(status_code==241) continue; if(status_code!=100) throw failed_xri_resolution(OPKELE_CP_ "XRI resolution failed with '"+status_string+"' message" ", while looking for SEP with type '"+st->uri+"'", status_code); if(idis.xrd.canonical_ids.empty()) throw opkele::failed_discovery(OPKELE_CP_ "No CanonicalID found for XRI identity found"); string cid = idis.xrd.canonical_ids.begin()->second; if(cids.find(cid)==cids.end()) { cids.insert(cid); idis.clear(); discover_at( idis, xri_proxy + util::url_encode(id)+ "?_xrd_t="+util::url_encode(st->uri)+ "&_xrd_r=application/xrd%2Bxml" ";sep=true;refs=true", xmode_xrd ); if(status_code==241) continue; if(status_code!=100) throw failed_xri_resolution(OPKELE_CP_ "XRI resolution failed with '"+status_string+"' message" ", while looking for SEP with type '"+st->uri+"'" " on canonical id", status_code); } idis.canonicalized_id = cid; idis.normalized_id = rv; idis.xri_identity = true; queue_endpoints(oi,idis,st); } }else{ idis.xri_identity = false; if(id.find("://")==string::npos) id.insert(0,"http://"); string::size_type fp = id.find('#'); if(fp!=string::npos) { string::size_type qp = id.find('?'); if(qp==string::npos || qp<fp) id.erase(fp); else if(qp>fp) id.erase(fp,qp-fp); } rv = idis.normalized_id = util::rfc_3986_normalize_uri(id); discover_at(idis,id,xmode_html|xmode_xrd); const char * eu = 0; CURLcode r = easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu); if(r) throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r); string cid = util::strip_uri_fragment_part( idis.canonicalized_id = util::rfc_3986_normalize_uri(eu) ); if(xrds_location.empty()) { html2xrd(oi,idis); }else{ idis.clear(); idis.canonicalized_id = cid; discover_at(idis,xrds_location,xmode_xrd); if(idis.xrd.empty()) html2xrd(oi,idis); else{ for(const service_type_t *st=op_service_types; st<&op_service_types[sizeof(op_service_types)/sizeof(*op_service_types)];++st) queue_endpoints(oi,idis,st); } } } return rv; } void discover_at(idiscovery_t& idis,const string& url,int xm) { CURLcode r = easy_setopt(CURLOPT_MAXREDIRS, (xm&xmode_noredirs)?0:5); if(r) throw exception_curl(OPKELE_CP_ "failed to set curly maxredirs option"); if( (r=easy_setopt(CURLOPT_URL,url.c_str())) ) throw exception_curl(OPKELE_CP_ "failed to set curly urlie",r); http_content_type.clear(); xmode = xm; prepare_to_parse(); if(xmode&xmode_html) { xrds_location.clear(); save_html.clear(); save_html.reserve(max_html); } xrd = &idis.xrd; r = easy_perform(); if(r && r!=CURLE_WRITE_ERROR) throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); if(!parser_choked) { parse(0,0,true); }else{ /* TODO: do not bother if we've seen xml */ try { util::tidy_doc_t td = util::tidy_doc_t::create(); if(!td) throw exception_tidy(OPKELE_CP_ "failed to create htmltidy document"); #ifndef NDEBUG td.opt_set(TidyQuiet,false); td.opt_set(TidyShowWarnings,false); #endif /* NDEBUG */ td.opt_set(TidyForceOutput,true); td.opt_set(TidyXhtmlOut,true); td.opt_set(TidyDoctypeMode,TidyDoctypeOmit); td.opt_set(TidyMark,false); if(td.parse_string(save_html)<=0) throw exception_tidy(OPKELE_CP_ "tidy failed to parse document"); if(td.clean_and_repair()<=0) throw exception_tidy(OPKELE_CP_ "tidy failed to clean and repair"); util::tidy_buf_t tide; if(td.save_buffer(tide)<=0) throw exception_tidy(OPKELE_CP_ "tidy failed to save buffer"); prepare_to_parse(); parse(tide.c_str(),tide.size(),true); }catch(exception_tidy& et) { } } save_html.clear(); } void prepare_to_parse() { (*(expat_t*)this) = parser_create_ns(); set_user_data(); set_element_handler(); set_character_data_handler(); if(xmode&xmode_html) { html_openid1.clear(); html_openid2.clear(); parser_choked = false; } cdata = 0; xrd_service = 0; skipping = 0; pt_stack.clear(); status_code = 100; status_string.clear(); } void html2xrd(endpoint_discovery_iterator& oi,idiscovery_t& id) { XRD_t& x = id.xrd; if(!html_openid2.uris.empty()) { html_openid2.types.insert(STURI_OPENID20); x.services.add(-1,html_openid2); queue_endpoints(oi,id,&op_service_types[st_index_2]); } if(!html_openid1.uris.empty()) { html_openid1.types.insert(STURI_OPENID11); x.services.add(-1,html_openid1); queue_endpoints(oi,id,&op_service_types[st_index_1]); } } size_t write(void *p,size_t s,size_t nm) { /* TODO: limit total size */ size_t bytes = s*nm; const char *inbuf = (const char*)p; if(xmode&xmode_html) { size_t mbts = save_html.capacity()-save_html.size(); size_t bts = 0; if(mbts>0) { bts = (bytes>mbts)?mbts:bytes; save_html.append(inbuf,bts); } if(skipping<0) return bts; } if(skipping<0) return 0; bool rp = parse(inbuf,bytes,false); if(!rp) { parser_choked = true; skipping = -1; if(!(xmode&xmode_html)) bytes = 0; } return bytes; } size_t header(void *p,size_t s,size_t nm) { size_t bytes = s*nm; const char *h = (const char*)p; const char *colon = (const char*)memchr(p,':',bytes); const char *space = (const char*)memchr(p,' ',bytes); if(space && ( (!colon) || space<colon ) ) { xrds_location.clear(); http_content_type.clear(); }else if(colon) { const char *hv = ++colon; - int hnl = colon-h; + size_t hnl = colon-h; int rb; for(rb = bytes-hnl-1;rb>0 && isspace(*hv);++hv,--rb); while(rb>0 && isspace(hv[rb-1])) --rb; if(rb) { if( (hnl>=sizeof(XRDS_HEADER)) && !strncasecmp(h,XRDS_HEADER":", sizeof(XRDS_HEADER)) ) { xrds_location.assign(hv,rb); }else if( (hnl>=sizeof(CT_HEADER)) && !strncasecmp(h,CT_HEADER":", sizeof(CT_HEADER)) ) { const char *sc = (const char*)memchr( hv,';',rb); http_content_type.assign(hv,sc?(sc-hv):rb); } } } return curl_t::header(p,s,nm); } void start_element(const XML_Char *n,const XML_Char **a) { if(skipping<0) return; if(skipping) { if(xmode&xmode_html) html_start_element(n,a); ++skipping; return; } if(pt_stack.empty()) { if(is_qelement(n,NSURI_XRDS "\tXRDS")) return; if(is_qelement(n,NSURI_XRD "\tXRD")) { assert(xrd); xrd->clear(); pt_stack.push_back(n); }else if(xmode&xmode_html) { html_start_element(n,a); }else{ skipping = -1; } }else{ int pt_s = pt_stack.size(); if(pt_s==1) { if(is_qelement(n,NSURI_XRD "\tCanonicalID")) { assert(xrd); cdata = &(xrd->canonical_ids.add(element_priority(a),string())); }else if(is_qelement(n,NSURI_XRD "\tLocalID")) { assert(xrd); cdata = &(xrd->local_ids.add(element_priority(a),string())); }else if(is_qelement(n,NSURI_XRD "\tProviderID")) { assert(xrd); cdata = &(xrd->provider_id); }else if(is_qelement(n,NSURI_XRD "\tService")) { assert(xrd); xrd_service = &(xrd->services.add(element_priority(a), service_t())); pt_stack.push_back(n); }else if(is_qelement(n,NSURI_XRD "\tStatus")) { for(;*a;) { if(!strcasecmp(*(a++),"code")) { if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) { cdata = &status_string; pt_stack.push_back(n); break; } }else ++a; } }else if(is_qelement(n,NSURI_XRD "\tExpires")) { assert(xrd); cdata_buf.clear(); cdata = &cdata_buf; }else if(xmode&xmode_html) { html_start_element(n,a); }else{ skipping = 1; } }else if(pt_s==2) { if(is_qelement(pt_stack.back().c_str(), NSURI_XRD "\tService")) { if(is_qelement(n,NSURI_XRD "\tType")) { assert(xrd); assert(xrd_service); cdata_buf.clear(); cdata = &cdata_buf; }else if(is_qelement(n,NSURI_XRD "\tURI")) { assert(xrd); assert(xrd_service); const char *append = element_attr(a,"append"); xrd::uri_t& uri = xrd_service->uris.add(element_priority(a),xrd::uri_t("",append?append:"")); cdata = &uri.uri; }else if(is_qelement(n,NSURI_XRD "\tLocalID") || is_qelement(n,NSURI_OPENID10 "\tDelegate") ) { assert(xrd); assert(xrd_service); cdata = &(xrd_service->local_ids.add(element_priority(a),string())); }else if(is_qelement(n,NSURI_XRD "\tProviderID")) { assert(xrd); assert(xrd_service); cdata = &(xrd_service->provider_id); }else{ skipping = 1; } }else skipping = 1; }else if(xmode&xmode_html) { html_start_element(n,a); }else{ skipping = 1; } } } void end_element(const XML_Char *n) { if(skipping<0) return; if(skipping) { --skipping; return; } if(is_qelement(n,NSURI_XRD "\tType")) { assert(xrd); assert(xrd_service); assert(cdata==&cdata_buf); xrd_service->types.insert(cdata_buf); }else if(is_qelement(n,NSURI_XRD "\tService")) { assert(xrd); assert(xrd_service); assert(!pt_stack.empty()); assert(pt_stack.back()==(NSURI_XRD "\tService")); pt_stack.pop_back(); xrd_service = 0; }else if(is_qelement(n,NSURI_XRD "\tStatus")) { assert(xrd); if(is_qelement(pt_stack.back().c_str(),n)) { assert(cdata==&status_string); pt_stack.pop_back(); if(status_code!=100) skipping = -1; } }else if(is_qelement(n,NSURI_XRD "\tExpires")) { assert(xrd); xrd->expires = util::w3c_to_time(cdata_buf); }else if((xmode&xmode_html) && is_element(n,"head")) { skipping = -1; } cdata = 0; } void character_data(const XML_Char *s,int l) { if(skipping) return; if(cdata) cdata->append(s,l); } void html_start_element(const XML_Char *n,const XML_Char **a) { if(is_element(n,"meta")) { bool heq = false; string l; for(;*a;a+=2) { if(!( strcasecmp(a[0],"http-equiv") || strcasecmp(a[1],XRDS_HEADER) )) heq = true; else if(!strcasecmp(a[0],"content")) l.assign(a[1]); } if(heq) xrds_location = l; }else if(is_element(n,"link")) { string rels; string href; for(;*a;a+=2) { if( !strcasecmp(a[0],"rel") ) { rels.assign(a[1]); }else if( !strcasecmp(a[0],"href") ) { const char *ns = a[1]; for(;*ns && isspace(*ns);++ns); href.assign(ns); string::size_type lns=href.find_last_not_of(whitespace); href.erase(lns+1); } } for(string::size_type ns=rels.find_first_not_of(whitespace); ns!=string::npos; ns=rels.find_first_not_of(whitespace,ns)) { string::size_type s = rels.find_first_of(whitespace,ns); string rel; if(s==string::npos) { rel.assign(rels,ns,string::npos); ns = string::npos; }else{ rel.assign(rels,ns,s-ns); ns = s; } if(rel=="openid.server") html_openid1.uris.add(-1,xrd::uri_t(href)); else if(rel=="openid.delegate") html_openid1.local_ids.add(-1,href); else if(rel=="openid2.provider") html_openid2.uris.add(-1,xrd::uri_t(href)); else if(rel=="openid2.local_id") html_openid2.local_ids.add(-1,href); } }else if(is_element(n,"body")) { skipping = -1; } } void queue_endpoints(endpoint_discovery_iterator& oi, const idiscovery_t &id, const service_type_t *st) { openid_endpoint_t ep; ep.claimed_id = id.canonicalized_id; for(xrd::services_t::const_iterator isvc=id.xrd.services.begin(); isvc!=id.xrd.services.end(); ++isvc) { const xrd::service_t svc = isvc->second; if(svc.types.find(st->uri)==svc.types.end()) continue; for(xrd::uris_t::const_iterator iu=svc.uris.begin();iu!=svc.uris.end();++iu) { ep.uri = iu->second.uri; if(id.xri_identity) { if(iu->second.append=="qxri") { ep.uri += id.normalized_id; } /* TODO: else handle other append attribute values */ } if(st->forceid) { ep.local_id = ep.claimed_id = st->forceid; *(oi++) = ep; }else{ if(svc.local_ids.empty()) { ep.local_id = ep.claimed_id; *(oi++) = ep; }else{ for(xrd::local_ids_t::const_iterator ilid=svc.local_ids.begin(); ilid!=svc.local_ids.end(); ++ilid) { ep.local_id = ilid->second; *(oi++) = ep; } } } } } } }; string idiscover(endpoint_discovery_iterator oi,const string& identity) { idigger_t idigger; return idigger.discover(oi,identity); } void yadiscover(endpoint_discovery_iterator oi,const string& yurl,const char **types,bool redirs) try { idigger_t idigger; idigger.yadiscover(oi,yurl,types,redirs); }catch(exception_curl& ec) { if(redirs || ec._error!=CURLE_TOO_MANY_REDIRECTS) throw; } } diff --git a/lib/expat.cc b/lib/expat.cc index fa6fdde..c4dab7e 100644 --- a/lib/expat.cc +++ b/lib/expat.cc @@ -1,96 +1,97 @@ #include <opkele/expat.h> namespace opkele { namespace util { expat_t::~expat_t() throw() { if(_x) XML_ParserFree(_x); } expat_t& expat_t::operator=(XML_Parser x) { if(_x) XML_ParserFree(_x); _x = x; + return *this; } static void _start_element(void* ud,const XML_Char *n,const XML_Char **a) { ((expat_t*)ud)->start_element(n,a); } static void _end_element(void *ud,const XML_Char *n) { ((expat_t*)ud)->end_element(n); } void expat_t::set_element_handler() { assert(_x); XML_SetElementHandler(_x,_start_element,_end_element); } static void _character_data(void *ud,const XML_Char *s,int l) { ((expat_t*)ud)->character_data(s,l); } void expat_t::set_character_data_handler() { assert(_x); XML_SetCharacterDataHandler(_x,_character_data); } static void _processing_instruction(void *ud,const XML_Char *t,const XML_Char *d) { ((expat_t*)ud)->processing_instruction(t,d); } void expat_t::set_processing_instruction_handler() { assert(_x); XML_SetProcessingInstructionHandler(_x,_processing_instruction); } static void _comment(void *ud,const XML_Char *d) { ((expat_t*)ud)->comment(d); } void expat_t::set_comment_handler() { assert(_x); XML_SetCommentHandler(_x,_comment); } static void _start_cdata_section(void *ud) { ((expat_t*)ud)->start_cdata_section(); } static void _end_cdata_section(void *ud) { ((expat_t*)ud)->end_cdata_section(); } void expat_t::set_cdata_section_handler() { assert(_x); XML_SetCdataSectionHandler(_x,_start_cdata_section,_end_cdata_section); } static void _default_handler(void *ud,const XML_Char *s,int l) { ((expat_t*)ud)->default_handler(s,l); } void expat_t::set_default_handler() { assert(_x); XML_SetDefaultHandler(_x,_default_handler); } void expat_t::set_default_handler_expand() { assert(_x); XML_SetDefaultHandlerExpand(_x,_default_handler); } static void _start_namespace_decl(void *ud,const XML_Char *p,const XML_Char *u) { ((expat_t*)ud)->start_namespace_decl(p,u); } static void _end_namespace_decl(void *ud,const XML_Char *p) { ((expat_t*)ud)->end_namespace_decl(p); } void expat_t::set_namespace_decl_handler() { assert(_x); XML_SetNamespaceDeclHandler(_x,_start_namespace_decl,_end_namespace_decl); } } } diff --git a/lib/extension.cc b/lib/extension.cc index f7aaea5..0f121ca 100644 --- a/lib/extension.cc +++ b/lib/extension.cc @@ -1,26 +1,26 @@ #include <opkele/exception.h> #include <opkele/extension.h> namespace opkele { void extension_t::rp_checkid_hook(basic_openid_message&) { throw not_implemented(OPKELE_CP_ "RP checkid_* hook not implemented"); } void extension_t::rp_id_res_hook(const basic_openid_message&, const basic_openid_message&) { throw not_implemented(OPKELE_CP_ "RP id_res hook not implemented"); } void extension_t::op_checkid_hook(const basic_openid_message&) { throw not_implemented(OPKELE_CP_ "OP checkid_* hook not implemented"); } - void extension_t::op_id_res_hook(basic_openid_message& om) { + void extension_t::op_id_res_hook(basic_openid_message&) { throw not_implemented(OPKELE_CP_ "OP id_res hook not implemented"); } void extension_t::checkid_hook(basic_openid_message&) { throw not_implemented(OPKELE_CP_ "deprecated consumer checkid_* hook not implemented"); } void extension_t::id_res_hook(const basic_openid_message&, const basic_openid_message&) { throw not_implemented(OPKELE_CP_ "deprecated consumer id_res hook not implemented"); } void extension_t::checkid_hook(const basic_openid_message&,basic_openid_message&) { throw not_implemented(OPKELE_CP_ "deprecated server checkid hook not implemented"); } } diff --git a/lib/openid_message.cc b/lib/openid_message.cc index fdb4b04..521ea85 100644 --- a/lib/openid_message.cc +++ b/lib/openid_message.cc @@ -1,265 +1,265 @@ #include <cassert> #include <opkele/types.h> #include <opkele/exception.h> #include <opkele/util.h> #include <opkele/debug.h> #include "config.h" namespace opkele { using std::input_iterator_tag; using std::unary_function; struct __om_copier : public unary_function<const string&,void> { public: const basic_openid_message& from; basic_openid_message& to; - __om_copier(basic_openid_message& to,const basic_openid_message& from) - : from(from), to(to) { + __om_copier(basic_openid_message& t,const basic_openid_message& f) + : from(f), to(t) { to.reset_fields(); } result_type operator()(argument_type f) { to.set_field(f,from.get_field(f)); } }; basic_openid_message::basic_openid_message(const basic_openid_message& x) { x.copy_to(*this); } void basic_openid_message::copy_to(basic_openid_message& x) const { for_each(fields_begin(),fields_end(), __om_copier(x,*this) ); } struct __om_ns_finder : public unary_function<const string&,bool> { public: const basic_openid_message& om; const string& uri; - __om_ns_finder(const basic_openid_message& om, - const string& uri) : om(om), uri(uri) { } + __om_ns_finder(const basic_openid_message& m, + const string& u) : om(m), uri(u) { } result_type operator()(argument_type f) { return (!strncmp(f.c_str(),"ns.",sizeof("ns.")-1)) && om.get_field(f)==uri ; } }; bool basic_openid_message::has_ns(const string& uri) const { fields_iterator ei = fields_end(); fields_iterator i = find_if(fields_begin(),fields_end(), __om_ns_finder(*this,uri)); return !(i==ei); } string basic_openid_message::get_ns(const string& uri) const { fields_iterator ei = fields_end(); fields_iterator i = find_if(fields_begin(),fields_end(), __om_ns_finder(*this,uri)); if(i==ei) throw failed_lookup(OPKELE_CP_ string("failed to find namespace ")+uri); return i->substr(3); } struct __om_query_builder : public unary_function<const string&,void> { public: const basic_openid_message& om; - string& rv; bool first; + string& rv; - __om_query_builder(string& rv,const basic_openid_message& om) - : om(om), first(true), rv(rv) { + __om_query_builder(string& r,const basic_openid_message& m) + : om(m), first(true), rv(r) { for_each(om.fields_begin(),om.fields_end(),*this); } - __om_query_builder(string& rv,const basic_openid_message& om,const string& url) - : om(om), first(true), rv(rv) { - rv = url; + __om_query_builder(string& r,const basic_openid_message& m,const string& u) + : om(m), first(true), rv(r) { + rv = u; if(rv.find('?')==string::npos) rv += '?'; else first = false; for_each(om.fields_begin(),om.fields_end(),*this); } result_type operator()(argument_type f) { if(first) first = false; else rv += '&'; rv += "openid."; rv+= f; rv += '='; rv += util::url_encode(om.get_field(f)); } }; string basic_openid_message::append_query(const string& url) const { string rv; return __om_query_builder(rv,*this,url).rv; } string basic_openid_message::query_string() const { string rv; return __om_query_builder(rv,*this).rv; } void basic_openid_message::reset_fields() { throw not_implemented(OPKELE_CP_ "reset_fields() not implemented"); } - void basic_openid_message::set_field(const string& n,const string& v) { + void basic_openid_message::set_field(const string&,const string&) { throw not_implemented(OPKELE_CP_ "set_field() not implemented"); } - void basic_openid_message::reset_field(const string& n) { + void basic_openid_message::reset_field(const string&) { throw not_implemented(OPKELE_CP_ "reset_field() not implemented"); } void basic_openid_message::from_keyvalues(const string& kv) { reset_fields(); string::size_type p = 0; while(true) { string::size_type co = kv.find(':',p); if(co==string::npos) break; #ifndef POSTELS_LAW string::size_type nl = kv.find('\n',co+1); if(nl==string::npos) throw bad_input(OPKELE_CP_ "malformed input"); if(nl>co) insert(value_type(kv.substr(p,co-p),kv.substr(co+1,nl-co-1))); p = nl+1; #else /* POSTELS_LAW */ string::size_type lb = kv.find_first_of("\r\n",co+1); if(lb==string::npos) { set_field(kv.substr(p,co-p),kv.substr(co+1)); break; } if(lb>co) set_field(kv.substr(p,co-p),kv.substr(co+1,lb-co-1)); string::size_type nolb = kv.find_first_not_of("\r\n",lb); if(nolb==string::npos) break; p = nolb; #endif /* POSTELS_LAW */ } } struct __om_kv_outputter : public unary_function<const string&,void> { public: const basic_openid_message& om; ostream& os; - __om_kv_outputter(const basic_openid_message& om,ostream& os) - : om(om), os(os) { } + __om_kv_outputter(const basic_openid_message& m,ostream& s) + : om(m), os(s) { } result_type operator()(argument_type f) { os << f << ':' << om.get_field(f) << '\n'; } }; void basic_openid_message::to_keyvalues(ostream& o) const { for_each(fields_begin(),fields_end(),__om_kv_outputter(*this,o)); } struct __om_html_outputter : public unary_function<const string&,void> { public: const basic_openid_message& om; ostream& os; - __om_html_outputter(const basic_openid_message& om,ostream& os) - : om(om), os(os) { } + __om_html_outputter(const basic_openid_message& m,ostream& s) + : om(m), os(s) { } result_type operator()(argument_type f) { os << "<input type=\"hidden\"" " name=\"" << util::attr_escape(f) << "\"" " value=\"" << util::attr_escape(om.get_field(f)) << "\" />"; } }; void basic_openid_message::to_htmlhiddens(ostream& o) const { for_each(fields_begin(),fields_end(),__om_html_outputter(*this,o)); } void basic_openid_message::add_to_signed(const string& fields) { string::size_type fnc = fields.find_first_not_of(","); if(fnc==string::npos) throw bad_input(OPKELE_CP_ "Trying to add nothing in particular to the list of signed fields"); string signeds; try { signeds = get_field("signed"); string::size_type lnc = signeds.find_last_not_of(","); if(lnc==string::npos) signeds.assign(fields,fnc,fields.size()-fnc); else{ string::size_type ss = signeds.size(); if(lnc==(ss-1)) { signeds+= ','; signeds.append(fields,fnc,fields.size()-fnc); }else{ if(lnc<(ss-2)) signeds.replace(lnc+2,ss-lnc-2, fields,fnc,fields.size()-fnc); else signeds.append(fields,fnc,fields.size()-fnc); } } }catch(failed_lookup&) { signeds.assign(fields,fnc,fields.size()-fnc); } set_field("signed",signeds); } string basic_openid_message::find_ns(const string& uri,const char *pfx) const { if(has_field("ns")) return get_ns(uri); return pfx; } string basic_openid_message::allocate_ns(const string& uri,const char *pfx) { if(!has_field("ns")) return pfx; if(has_ns(uri)) throw bad_input(OPKELE_CP_ "OpenID message already contains namespace"); string rv = pfx; if(has_field("ns."+rv)) { string::reference c=rv[rv.length()]; for(c='a';c<='z' && has_field("ns."+rv);++c); if(c=='z') throw exception(OPKELE_CP_ "Failed to allocate namespace"); } set_field("ns."+rv,uri); return rv; } void openid_message_t::copy_to(basic_openid_message& x) const { x.reset_fields(); for(const_iterator i=begin();i!=end();++i) x.set_field(i->first,i->second); } bool openid_message_t::has_field(const string& n) const { return find(n)!=end(); } const string& openid_message_t::get_field(const string& n) const { const_iterator i=find(n); if(i==end()) throw failed_lookup(OPKELE_CP_ n+": no such field"); return i->second; } openid_message_t::fields_iterator openid_message_t::fields_begin() const { return util::map_keys_iterator<const_iterator,string,const string&,const string*>(begin(),end()); } openid_message_t::fields_iterator openid_message_t::fields_end() const { return util::map_keys_iterator<const_iterator,string,const string&,const string*>(end(),end()); } void openid_message_t::reset_fields() { clear(); } void openid_message_t::set_field(const string& n,const string& v) { (*this)[n]=v; } void openid_message_t::reset_field(const string& n) { erase(n); } } diff --git a/lib/prequeue_rp.cc b/lib/prequeue_rp.cc index e242f87..3aa960f 100644 --- a/lib/prequeue_rp.cc +++ b/lib/prequeue_rp.cc @@ -1,81 +1,81 @@ #include <iostream> #include <openssl/sha.h> #include <openssl/hmac.h> #include <opkele/exception.h> #include <opkele/prequeue_rp.h> #include <opkele/discovery.h> #include <opkele/uris.h> #include <opkele/data.h> #include <opkele/util.h> #include <opkele/curl.h> #include <opkele/debug.h> namespace opkele { class __OP_verifier_good_input : public exception { public: __OP_verifier_good_input(OPKELE_E_PARS) : exception(OPKELE_E_CONS) { } }; class OP_verifier : public iterator<output_iterator_tag,openid_endpoint_t,void> { public: const string& OP; const string& id; OP_verifier(const string& o,const string& i) : OP(o), id(i) { } OP_verifier& operator*() { return *this; } OP_verifier& operator=(const openid_endpoint_t& oep) { if(oep.uri==OP) { if(oep.claimed_id==IDURI_SELECT20 || oep.local_id==IDURI_SELECT20 ) throw bad_input(OPKELE_CP_ "claimed_id is an OP-Id"); if(oep.local_id==id) throw __OP_verifier_good_input(OPKELE_CP_ "Found corresponding endpoint"); } return *this; } OP_verifier& operator++() { return *this; } OP_verifier& operator++(int) { return *this; } }; void prequeue_RP::verify_OP(const string& OP,const string& claimed_id,const string& identity) const { try { idiscover(OP_verifier(OP,identity),claimed_id); throw id_res_unauthorized(OPKELE_CP_ "OP is not authorized to make an assertion regarding the identity"); }catch(__OP_verifier_good_input& ovgi) { } } class endpoint_queuer : public iterator<output_iterator_tag,openid_endpoint_t,void> { public: prequeue_RP& rp; - endpoint_queuer(prequeue_RP& rp) : rp(rp) { } + endpoint_queuer(prequeue_RP& r) : rp(r) { } endpoint_queuer& operator*() { return *this; } endpoint_queuer& operator=(const openid_endpoint_t& oep) { rp.queue_endpoint(oep); return *this; } endpoint_queuer& operator++() { return *this; } endpoint_queuer& operator++(int) { return *this; } }; void prequeue_RP::initiate(const string& usi) { begin_queueing(); set_normalized_id( idiscover(endpoint_queuer(*this),usi) ); end_queueing(); } - void prequeue_RP::set_normalized_id(const string& nid) { + void prequeue_RP::set_normalized_id(const string&) { } const string prequeue_RP::get_normalized_id() const { throw not_implemented(OPKELE_CP_ "get_normalized_id() is not implemented"); } } diff --git a/lib/sreg.cc b/lib/sreg.cc index b40cd45..0bd4d2e 100644 --- a/lib/sreg.cc +++ b/lib/sreg.cc @@ -1,160 +1,158 @@ #include <opkele/exception.h> #include <opkele/sreg.h> #include <opkele/uris.h> #include <algorithm> namespace opkele { using std::find; static const struct _sreg_field { const char *fieldname; sreg_t::fieldbit_t fieldbit; } fields[] = { { "nickname", sreg_t::field_nickname }, { "email", sreg_t::field_email }, { "fullname", sreg_t::field_fullname }, { "dob", sreg_t::field_dob }, { "gender", sreg_t::field_gender }, { "postcode", sreg_t::field_postcode }, { "country", sreg_t::field_country }, { "language", sreg_t::field_language }, { "timezone", sreg_t::field_timezone } }; # define fields_BEGIN fields # define fields_END &fields[sizeof(fields)/sizeof(*fields)] typedef const struct _sreg_field *fields_iterator; bool operator==(const struct _sreg_field& fd,const string& fn) { return fd.fieldname==fn; } void sreg_t::rp_checkid_hook(basic_openid_message& om) { string fr, fo; for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { if(f->fieldbit&fields_required) { if(!fr.empty()) fr+=","; fr += f->fieldname; } if(f->fieldbit&fields_optional) { if(!fo.empty()) fo+=","; fo += f->fieldname; } } string pfx = om.allocate_ns(OIURI_SREG11,"sreg"); if(!fr.empty()) om.set_field(pfx+".required",fr); if(!fo.empty()) om.set_field(pfx+".optional",fo); if(!policy_url.empty()) om.set_field(pfx+".policy_url",policy_url); } void sreg_t::checkid_hook(basic_openid_message& om) { rp_checkid_hook(om); } void sreg_t::rp_id_res_hook(const basic_openid_message& om, const basic_openid_message& sp) { clear(); string pfx; try { pfx = om.find_ns(OIURI_SREG11,"sreg"); - }catch(failed_lookup& fl) { + }catch(failed_lookup&) { try { pfx = om.find_ns(OIURI_SREG10,"sreg"); - }catch(failed_lookup& fl) { - return; - } + }catch(failed_lookup&) { return; } } pfx += '.'; for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { string fn = pfx; fn+=f->fieldname; if(!sp.has_field(fn)) continue; has_fields |= f->fieldbit; response[f->fieldbit]=sp.get_field(fn); } } void sreg_t::id_res_hook(const basic_openid_message& om, const basic_openid_message& sp) { rp_id_res_hook(om,sp); } const string& sreg_t::get_field(fieldbit_t fb) const { response_t::const_iterator i = response.find(fb); if(i==response.end()) throw failed_lookup(OPKELE_CP_ "no field data available"); return i->second; } void sreg_t::set_field(fieldbit_t fb,const string& fv) { response[fb] = fv; has_fields |= fb; } void sreg_t::reset_field(fieldbit_t fb) { has_fields &= ~fb; response.erase(fb); } void sreg_t::clear() { has_fields = 0; response.clear(); } static long fields_list_to_bitmask(string& fl) { long rv = 0; while(!fl.empty()) { string::size_type co = fl.find(','); string fn; if(co==string::npos) { fn = fl; fl.erase(); }else{ fn = fl.substr(0,co); fl.erase(0,co+1); } fields_iterator f = find(fields_BEGIN,fields_END,fn); if(f!=fields_END) rv |= f->fieldbit; } return rv; } void sreg_t::op_checkid_hook(const basic_openid_message& inm) { string ins = inm.find_ns(OIURI_SREG11,"sreg"); fields_optional = 0; fields_required = 0; policy_url.erase(); fields_response = 0; try { string fl = inm.get_field(ins+".required"); fields_required = fields_list_to_bitmask(fl); }catch(failed_lookup&) { } try { string fl = inm.get_field(ins+".optional"); fields_optional = fields_list_to_bitmask(fl); }catch(failed_lookup&) { } try { policy_url = inm.get_field(ins+".policy_url"); }catch(failed_lookup&) { } } void sreg_t::op_id_res_hook(basic_openid_message& oum) { string ons = oum.allocate_ns(OIURI_SREG11,"sreg"); fields_response &= has_fields; string signeds = "ns."+ons; for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { if(!(f->fieldbit&fields_response)) continue; signeds +=','; string pn = ons; pn += '.'; pn += f->fieldname; signeds += pn; oum.set_field(pn,get_field(f->fieldbit)); } oum.add_to_signed(signeds); } void sreg_t::checkid_hook(const basic_openid_message& inm, basic_openid_message& oum) { op_checkid_hook(inm); setup_response(inm,oum); op_id_res_hook(oum); } void sreg_t::setup_response(const basic_openid_message& /* inm */,basic_openid_message& /* oum */) { setup_response(); } void sreg_t::setup_response() { fields_response = (fields_required|fields_optional)&has_fields; } } diff --git a/lib/verify_op.cc b/lib/verify_op.cc index ab21b4f..c493c12 100644 --- a/lib/verify_op.cc +++ b/lib/verify_op.cc @@ -1,53 +1,53 @@ #include <opkele/verify_op.h> #include <opkele/discovery.h> #include <opkele/exception.h> #include <opkele/util.h> #include <opkele/uris.h> namespace opkele { using std::output_iterator_tag; class __RP_verifier_good_input : public exception { public: __RP_verifier_good_input(OPKELE_E_PARS) : exception(OPKELE_E_CONS) { } }; class RP_verifier : public iterator<output_iterator_tag,openid_endpoint_t,void> { public: - int seen; const string& return_to; + int seen; RP_verifier(const string& rt) : return_to(rt), seen(0) { } RP_verifier& operator*() { return *this; } RP_verifier& operator=(const openid_endpoint_t& oep) { if(util::uri_matches_realm(return_to,oep.uri)) throw __RP_verifier_good_input(OPKELE_CP_ "Found matching realm"); return *this; } RP_verifier& operator++() { ++seen; return *this; } - RP_verifier& operator++(int) { +seen; return *this; } + RP_verifier& operator++(int) { ++seen; return *this; } }; void verify_OP::verify_return_to() { basic_OP::verify_return_to(); try { RP_verifier rpv(return_to); string drealm = realm; string::size_type csss = drealm.find("://*."); if(csss==4 || csss==5) drealm.replace(csss+3,1,"www"); const char *rtt[] = { STURI_OPENID20_RT, 0 }; yadiscover(rpv,drealm,rtt,false); if(rpv.seen) throw bad_return_to(OPKELE_CP_ "return_to URL doesn't match any found while doing discovery on RP"); }catch(__RP_verifier_good_input&) { }catch(bad_return_to& brt) { throw; }catch(exception_network&) { } } } |