summaryrefslogtreecommitdiffabout
path: root/lib/basic_rp.cc
authorMichael Krelin <hacker@klever.net>2008-01-23 21:13:27 (UTC)
committer Michael Krelin <hacker@klever.net>2008-01-23 21:13:27 (UTC)
commitdc5dad0709ae8dd0a4be74248a22a78e47ba90e2 (patch) (unidiff)
treeeebd2a18bec33c4250036e1fd92d38cd4268777e /lib/basic_rp.cc
parent2a116bce75236e46946bb9564790c26c6a59b9a4 (diff)
downloadlibopkele-dc5dad0709ae8dd0a4be74248a22a78e47ba90e2.zip
libopkele-dc5dad0709ae8dd0a4be74248a22a78e47ba90e2.tar.gz
libopkele-dc5dad0709ae8dd0a4be74248a22a78e47ba90e2.tar.bz2
check associate reply for consistency
Reject associate replies returning secret of inconsistent with association type length. This way severely broken OPs which return SHA1 association as SHA256 will still work in dumb mode. Signed-off-by: Michael Krelin <hacker@klever.net>
Diffstat (limited to 'lib/basic_rp.cc') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/basic_rp.cc9
1 files changed, 6 insertions, 3 deletions
diff --git a/lib/basic_rp.cc b/lib/basic_rp.cc
index 763a391..2da8416 100644
--- a/lib/basic_rp.cc
+++ b/lib/basic_rp.cc
@@ -1,117 +1,120 @@
1#include <openssl/sha.h> 1#include <openssl/sha.h>
2#include <openssl/hmac.h> 2#include <openssl/hmac.h>
3#include <opkele/basic_rp.h> 3#include <opkele/basic_rp.h>
4#include <opkele/exception.h> 4#include <opkele/exception.h>
5#include <opkele/uris.h> 5#include <opkele/uris.h>
6#include <opkele/data.h> 6#include <opkele/data.h>
7#include <opkele/util.h> 7#include <opkele/util.h>
8#include <opkele/curl.h> 8#include <opkele/curl.h>
9 9
10namespace opkele { 10namespace opkele {
11 11
12 static void dh_get_secret( 12 static void dh_get_secret(
13 secret_t& secret, const basic_openid_message& om, 13 secret_t& secret, const basic_openid_message& om,
14 const char *exp_assoc, const char *exp_sess, 14 const char *exp_assoc, const char *exp_sess,
15 util::dh_t& dh, 15 util::dh_t& dh,
16 size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*) ) try { 16 size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*),
17 size_t exp_s_len) try {
17 if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess) 18 if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess)
18 throw bad_input(OPKELE_CP_ "Unexpected associate response"); 19 throw bad_input(OPKELE_CP_ "Unexpected associate response");
19 util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public")); 20 util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public"));
20 vector<unsigned char> ck(DH_size(dh)+1); 21 vector<unsigned char> ck(DH_size(dh)+1);
21 unsigned char *ckptr = &(ck.front())+1; 22 unsigned char *ckptr = &(ck.front())+1;
22 int cklen = DH_compute_key(ckptr,s_pub,dh); 23 int cklen = DH_compute_key(ckptr,s_pub,dh);
23 if(cklen<0) 24 if(cklen<0)
24 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); 25 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()");
25 if(cklen && (*ckptr)&0x80) { 26 if(cklen && (*ckptr)&0x80) {
26 (*(--ckptr))=0; ++cklen; } 27 (*(--ckptr))=0; ++cklen; }
27 unsigned char key_digest[d_len]; 28 unsigned char key_digest[d_len];
28 secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key")); 29 secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key"));
30 if(secret.size()!=exp_s_len)
31 throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type");
29 }catch(opkele::failed_lookup& ofl) { 32 }catch(opkele::failed_lookup& ofl) {
30 throw bad_input(OPKELE_CP_ "Incoherent response from OP"); 33 throw bad_input(OPKELE_CP_ "Incoherent response from OP");
31 } OPKELE_RETHROW 34 } OPKELE_RETHROW
32 35
33 static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) { 36 static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) {
34 util::curl_pick_t curl = util::curl_pick_t::easy_init(); 37 util::curl_pick_t curl = util::curl_pick_t::easy_init();
35 if(!curl) 38 if(!curl)
36 throw exception_curl(OPKELE_CP_ "failed to initialize curl"); 39 throw exception_curl(OPKELE_CP_ "failed to initialize curl");
37 string request = inm.query_string(); 40 string request = inm.query_string();
38 CURLcode r; 41 CURLcode r;
39 (r=curl.misc_sets()) 42 (r=curl.misc_sets())
40 || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str())) 43 || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str()))
41 || (r=curl.easy_setopt(CURLOPT_POST,1)) 44 || (r=curl.easy_setopt(CURLOPT_POST,1))
42 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) 45 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data()))
43 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) 46 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length()))
44 || (r=curl.set_write()); 47 || (r=curl.set_write());
45 if(r) 48 if(r)
46 throw exception_curl(OPKELE_CP_ "failed to set curly options",r); 49 throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
47 if( (r=curl.easy_perform()) ) 50 if( (r=curl.easy_perform()) )
48 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); 51 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
49 oum.from_keyvalues(curl.response); 52 oum.from_keyvalues(curl.response);
50 } 53 }
51 54
52 55
53 assoc_t basic_RP::associate(const string& OP) { 56 assoc_t basic_RP::associate(const string& OP) {
54 util::dh_t dh = DH_new(); 57 util::dh_t dh = DH_new();
55 if(!dh) 58 if(!dh)
56 throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); 59 throw exception_openssl(OPKELE_CP_ "failed to DH_new()");
57 dh->p = util::dec_to_bignum(data::_default_p); 60 dh->p = util::dec_to_bignum(data::_default_p);
58 dh->g = util::dec_to_bignum(data::_default_g); 61 dh->g = util::dec_to_bignum(data::_default_g);
59 if(!DH_generate_key(dh)) 62 if(!DH_generate_key(dh))
60 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); 63 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()");
61 openid_message_t req; 64 openid_message_t req;
62 req.set_field("ns",OIURI_OPENID20); 65 req.set_field("ns",OIURI_OPENID20);
63 req.set_field("mode","associate"); 66 req.set_field("mode","associate");
64 req.set_field("dh_modulus",util::bignum_to_base64(dh->p)); 67 req.set_field("dh_modulus",util::bignum_to_base64(dh->p));
65 req.set_field("dh_gen",util::bignum_to_base64(dh->g)); 68 req.set_field("dh_gen",util::bignum_to_base64(dh->g));
66 req.set_field("dh_consumer_public",util::bignum_to_base64(dh->pub_key)); 69 req.set_field("dh_consumer_public",util::bignum_to_base64(dh->pub_key));
67 openid_message_t res; 70 openid_message_t res;
68 req.set_field("assoc_type","HMAC-SHA256"); 71 req.set_field("assoc_type","HMAC-SHA256");
69 req.set_field("session_type","DH-SHA256"); 72 req.set_field("session_type","DH-SHA256");
70 secret_t secret; 73 secret_t secret;
71 int expires_in; 74 int expires_in;
72 try { 75 try {
73 direct_request(res,req,OP); 76 direct_request(res,req,OP);
74 dh_get_secret( secret, res, 77 dh_get_secret( secret, res,
75 "HMAC-SHA256", "DH-SHA256", 78 "HMAC-SHA256", "DH-SHA256",
76 dh, SHA256_DIGEST_LENGTH, SHA256 ); 79 dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH );
77 expires_in = util::string_to_long(res.get_field("expires_in")); 80 expires_in = util::string_to_long(res.get_field("expires_in"));
78 }catch(exception& e) { 81 }catch(exception& e) {
79 try { 82 try {
80 req.set_field("assoc_type","HMAC-SHA1"); 83 req.set_field("assoc_type","HMAC-SHA1");
81 req.set_field("session_type","DH-SHA1"); 84 req.set_field("session_type","DH-SHA1");
82 direct_request(res,req,OP); 85 direct_request(res,req,OP);
83 dh_get_secret( secret, res, 86 dh_get_secret( secret, res,
84 "HMAC-SHA1", "DH-SHA1", 87 "HMAC-SHA1", "DH-SHA1",
85 dh, SHA_DIGEST_LENGTH, SHA1 ); 88 dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH );
86 expires_in = util::string_to_long(res.get_field("expires_in")); 89 expires_in = util::string_to_long(res.get_field("expires_in"));
87 }catch(bad_input& e) { 90 }catch(bad_input& e) {
88 throw dumb_RP(OPKELE_CP_ "OP failed to supply an association"); 91 throw dumb_RP(OPKELE_CP_ "OP failed to supply an association");
89 } 92 }
90 } 93 }
91 return store_assoc( 94 return store_assoc(
92 OP, res.get_field("assoc_handle"), 95 OP, res.get_field("assoc_handle"),
93 res.get_field("assoc_type"), secret, 96 res.get_field("assoc_type"), secret,
94 expires_in ); 97 expires_in );
95 } 98 }
96 99
97 basic_openid_message& basic_RP::checkid_( 100 basic_openid_message& basic_RP::checkid_(
98 basic_openid_message& rv, 101 basic_openid_message& rv,
99 mode_t mode, 102 mode_t mode,
100 const string& return_to,const string& realm, 103 const string& return_to,const string& realm,
101 extension_t *ext) { 104 extension_t *ext) {
102 rv.reset_fields(); 105 rv.reset_fields();
103 rv.set_field("ns",OIURI_OPENID20); 106 rv.set_field("ns",OIURI_OPENID20);
104 if(mode==mode_checkid_immediate) 107 if(mode==mode_checkid_immediate)
105 rv.set_field("mode","checkid_immediate"); 108 rv.set_field("mode","checkid_immediate");
106 else if(mode==mode_checkid_setup) 109 else if(mode==mode_checkid_setup)
107 rv.set_field("mode","checkid_setup"); 110 rv.set_field("mode","checkid_setup");
108 else 111 else
109 throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); 112 throw bad_input(OPKELE_CP_ "unknown checkid_* mode");
110 if(realm.empty() && return_to.empty()) 113 if(realm.empty() && return_to.empty())
111 throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty"); 114 throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty");
112 if(!realm.empty()) { 115 if(!realm.empty()) {
113 rv.set_field("realm",realm); 116 rv.set_field("realm",realm);
114 rv.set_field("trust_root",realm); 117 rv.set_field("trust_root",realm);
115 } 118 }
116 if(!return_to.empty()) 119 if(!return_to.empty())
117 rv.set_field("return_to",return_to); 120 rv.set_field("return_to",return_to);