-rw-r--r-- | lib/consumer.cc | 13 | ||||
-rw-r--r-- | lib/server.cc | 11 | ||||
-rw-r--r-- | lib/util.cc | 10 |
3 files changed, 20 insertions, 14 deletions
diff --git a/lib/consumer.cc b/lib/consumer.cc index 12866f0..282f0cc 100644 --- a/lib/consumer.cc +++ b/lib/consumer.cc @@ -54,105 +54,106 @@ namespace opkele { size_t get = min(16384-str->length(),bytes); str->append((const char*)ptr,get); return get; } assoc_t consumer_t::associate(const string& server) { 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()"); string request = "openid.mode=associate" "&openid.assoc_type=HMAC-SHA1" "&openid.session_type=DH-SHA1" "&openid.dh_consumer_public="; request += util::url_encode(util::bignum_to_base64(dh->pub_key)); curl_t curl = curl_easy_init(); if(!curl) throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()"); string response; CURLcode r; (r=curl_misc_sets(curl)) || (r=curl_easy_setopt(curl,CURLOPT_URL,server.c_str())) || (r=curl_easy_setopt(curl,CURLOPT_POST,1)) || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDS,request.data())) || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDSIZE,request.length())) || (r=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curl_tostring)) || (r=curl_easy_setopt(curl,CURLOPT_WRITEDATA,&response)) ; if(r) throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r); if(r=curl_easy_perform(curl)) throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r); params_t p; p.parse_keyvalues(response); if(p.has_param("assoc_type") && p.get_param("assoc_type")!="HMAC-SHA1") throw bad_input(OPKELE_CP_ "unsupported assoc_type"); string st; if(p.has_param("session_type")) st = p.get_param("session_type"); if((!st.empty()) && st!="DH-SHA1") throw bad_input(OPKELE_CP_ "unsupported session_type"); secret_t secret; if(st.empty()) { secret.from_base64(p.get_param("mac_key")); }else{ util::bignum_t s_pub = util::base64_to_bignum(p.get_param("dh_server_public")); - vector<unsigned char> ck(DH_size(dh)); - int cklen = DH_compute_key(&(ck.front()),s_pub,dh); + 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()"); - ck.resize(cklen); - // OpenID algorithm requires extra zero in case of set bit here - if(ck[0]&0x80) ck.insert(ck.begin(),1,0); + if(cklen && (*ckptr)&0x80) { + (*(--ckptr)) = 0; ++cklen; + } unsigned char key_sha1[SHA_DIGEST_LENGTH]; - SHA1(&(ck.front()),ck.size(),key_sha1); + SHA1(ckptr,cklen,key_sha1); secret.enxor_from_base64(key_sha1,p.get_param("enc_mac_key")); } int expires_in = 0; if(p.has_param("expires_in")) { expires_in = util::string_to_long(p.get_param("expires_in")); }else if(p.has_param("issued") && p.has_param("expiry")) { expires_in = util::w3c_to_time(p.get_param("expiry"))-util::w3c_to_time(p.get_param("issued")); }else throw bad_input(OPKELE_CP_ "no expiration information"); return store_assoc(server,p.get_param("assoc_handle"),secret,expires_in); } string consumer_t::checkid_immediate(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { return checkid_(mode_checkid_immediate,identity,return_to,trust_root,ext); } string consumer_t::checkid_setup(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { return checkid_(mode_checkid_setup,identity,return_to,trust_root,ext); } string consumer_t::checkid_(mode_t mode,const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { params_t p; if(mode==mode_checkid_immediate) p["mode"]="checkid_immediate"; else if(mode==mode_checkid_setup) p["mode"]="checkid_setup"; else throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); string iurl = canonicalize(identity); string server, delegate; retrieve_links(iurl,server,delegate); p["identity"] = delegate.empty()?iurl:delegate; if(!trust_root.empty()) p["trust_root"] = trust_root; p["return_to"] = return_to; try { string ah = find_assoc(server)->handle(); p["assoc_handle"] = ah; }catch(failed_lookup& fl) { string ah = associate(server)->handle(); p["assoc_handle"] = ah; } if(ext) ext->checkid_hook(p,identity); return p.append_query(server); } void consumer_t::id_res(const params_t& pin,const string& identity,extension_t *ext) { if(pin.has_param("openid.user_setup_url")) throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",pin.get_param("openid.user_setup_url")); string server,delegate; diff --git a/lib/server.cc b/lib/server.cc index e81d4b6..8db97be 100644 --- a/lib/server.cc +++ b/lib/server.cc @@ -1,92 +1,93 @@ #include <vector> #include <openssl/sha.h> #include <openssl/hmac.h> #include <opkele/util.h> #include <opkele/exception.h> #include <opkele/server.h> #include <opkele/data.h> namespace opkele { using namespace std; void server_t::associate(const params_t& pin,params_t& pout) { util::dh_t dh; util::bignum_t c_pub; unsigned char key_sha1[SHA_DIGEST_LENGTH]; enum { sess_cleartext, sess_dh_sha1 } st = sess_cleartext; if( pin.has_param("openid.session_type") && pin.get_param("openid.session_type")=="DH-SHA1" ) { /* TODO: fallback to cleartext in case of exceptions here? */ if(!(dh = DH_new())) throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); c_pub = util::base64_to_bignum(pin.get_param("openid.dh_consumer_public")); if(pin.has_param("openid.dh_modulus")) dh->p = util::base64_to_bignum(pin.get_param("openid.dh_modulus")); else dh->p = util::dec_to_bignum(data::_default_p); if(pin.has_param("openid.dh_gen")) dh->g = util::base64_to_bignum(pin.get_param("openid.dh_gen")); else 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)); + vector<unsigned char> ck(DH_size(dh)+1); + unsigned char *ckptr = &(ck.front())+1; int cklen = DH_compute_key(&(ck.front()),c_pub,dh); if(cklen<0) throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); - ck.resize(cklen); - // OpenID algorithm requires extra zero in case of set bit here - if(ck[0]&0x80) ck.insert(ck.begin(),1,0); - SHA1(&(ck.front()),ck.size(),key_sha1); + if(cklen && (*ckptr)&0x80) { + (*(--ckptr)) = 0; ++cklen; + } + SHA1(ckptr,cklen,key_sha1); st = sess_dh_sha1; } assoc_t assoc = alloc_assoc(mode_associate); time_t now = time(0); pout.clear(); pout["assoc_type"] = assoc->assoc_type(); pout["assoc_handle"] = assoc->handle(); /* TODO: eventually remove deprecated stuff */ pout["issued"] = util::time_to_w3c(now); pout["expiry"] = util::time_to_w3c(now+assoc->expires_in()); pout["expires_in"] = util::long_to_string(assoc->expires_in()); secret_t secret = assoc->secret(); switch(st) { case sess_dh_sha1: pout["session_type"] = "DH-SHA1"; pout["dh_server_public"] = util::bignum_to_base64(dh->pub_key); secret.enxor_to_base64(key_sha1,pout["enc_mac_key"]); break; default: secret.to_base64(pout["mac_key"]); break; } } void server_t::checkid_immediate(const params_t& pin,string& return_to,params_t& pout,extension_t *ext) { checkid_(mode_checkid_immediate,pin,return_to,pout,ext); } void server_t::checkid_setup(const params_t& pin,string& return_to,params_t& pout,extension_t *ext) { checkid_(mode_checkid_setup,pin,return_to,pout,ext); } void server_t::checkid_(mode_t mode,const params_t& pin,string& return_to,params_t& pout,extension_t *ext) { if(mode!=mode_checkid_immediate && mode!=mode_checkid_setup) throw bad_input(OPKELE_CP_ "invalid checkid_* mode"); pout.clear(); assoc_t assoc; try { assoc = retrieve_assoc(pin.get_param("openid.assoc_handle")); }catch(failed_lookup& fl) { // no handle specified or no valid handle found, going dumb assoc = alloc_assoc(mode_checkid_setup); if(pin.has_param("openid.assoc_handle")) pout["invalidate_handle"]=pin.get_param("openid.assoc_handle"); } string trust_root; try { trust_root = pin.get_param("openid.trust_root"); diff --git a/lib/util.cc b/lib/util.cc index d9abca7..94f6f53 100644 --- a/lib/util.cc +++ b/lib/util.cc @@ -41,99 +41,103 @@ namespace opkele { throw; } } void decode_base64(const string& data,vector<unsigned char>& rv) { BIO *b64 = 0, *bmem = 0; rv.clear(); try { bmem = BIO_new_mem_buf((void*)data.data(),data.size()); if(!bmem) throw exception_openssl(OPKELE_CP_ "failed to BIO_new_mem_buf()"); b64 = BIO_new(BIO_f_base64()); if(!b64) throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 decoder"); BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL); BIO_push(b64,bmem); unsigned char tmp[512]; size_t rb = 0; while((rb=BIO_read(b64,tmp,sizeof(tmp)))>0) rv.insert(rv.end(),tmp,&tmp[rb]); BIO_free_all(b64); }catch(...) { if(b64) BIO_free_all(b64); throw; } } /* * big numerics */ BIGNUM *base64_to_bignum(const string& b64) { vector<unsigned char> bin; decode_base64(b64,bin); BIGNUM *rv = BN_bin2bn(&(bin.front()),bin.size(),0); if(!rv) throw failed_conversion(OPKELE_CP_ "failed to BN_bin2bn()"); return rv; } BIGNUM *dec_to_bignum(const string& dec) { BIGNUM *rv = 0; if(!BN_dec2bn(&rv,dec.c_str())) throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()"); return rv; } string bignum_to_base64(const BIGNUM *bn) { - vector<unsigned char> bin(BN_num_bytes(bn)); - int l = BN_bn2bin(bn,&(bin.front())); - return encode_base64(&(bin.front()),l); + vector<unsigned char> bin(BN_num_bytes(bn)+1); + unsigned char *binptr = &(bin.front())+1; + int l = BN_bn2bin(bn,binptr); + if(l && (*binptr)&0x80){ + (*(--binptr)) = 0; ++l; + } + return encode_base64(binptr,l); } /* * w3c times */ string time_to_w3c(time_t t) { struct tm tm_t; if(!gmtime_r(&t,&tm_t)) throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()"); char rv[25]; if(!strftime(rv,sizeof(rv)-1,"%Y-%m-%dT%H:%M:%SZ",&tm_t)) throw failed_conversion(OPKELE_CP_ "failed to strftime()"); return rv; } time_t w3c_to_time(const string& w) { struct tm tm_t; memset(&tm_t,0,sizeof(tm_t)); if( sscanf( w.c_str(), "%04d-%02d-%02dT%02d:%02d:%02dZ", &tm_t.tm_year,&tm_t.tm_mon,&tm_t.tm_mday, &tm_t.tm_hour,&tm_t.tm_min,&tm_t.tm_sec ) != 6 ) throw failed_conversion(OPKELE_CP_ "failed to sscanf()"); tm_t.tm_mon--; tm_t.tm_year-=1900; time_t rv = mktime(&tm_t); if(rv==(time_t)-1) throw failed_conversion(OPKELE_CP_ "failed to mktime()"); return rv; } /* * */ string url_encode(const string& str) { char * t = curl_escape(str.c_str(),str.length()); if(!t) throw failed_conversion(OPKELE_CP_ "failed to curl_escape()"); string rv(t); curl_free(t); return rv; } |