summaryrefslogtreecommitdiffabout
path: root/lib/basic_rp.cc
Unidiff
Diffstat (limited to 'lib/basic_rp.cc') (more/less context) (show whitespace changes)
-rw-r--r--lib/basic_rp.cc1
1 files changed, 1 insertions, 0 deletions
diff --git a/lib/basic_rp.cc b/lib/basic_rp.cc
index a0ad130..e65d9fb 100644
--- a/lib/basic_rp.cc
+++ b/lib/basic_rp.cc
@@ -1,200 +1,201 @@
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/curl.h> 10#include <opkele/curl.h>
10 11
11namespace opkele { 12namespace opkele {
12 13
13 static void dh_get_secret( 14 static void dh_get_secret(
14 secret_t& secret, const basic_openid_message& om, 15 secret_t& secret, const basic_openid_message& om,
15 const char *exp_assoc, const char *exp_sess, 16 const char *exp_assoc, const char *exp_sess,
16 util::dh_t& dh, 17 util::dh_t& dh,
17 size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*), 18 size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*),
18 size_t exp_s_len) try { 19 size_t exp_s_len) try {
19 if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess) 20 if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess)
20 throw bad_input(OPKELE_CP_ "Unexpected associate response"); 21 throw bad_input(OPKELE_CP_ "Unexpected associate response");
21 util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public")); 22 util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public"));
22 vector<unsigned char> ck(DH_size(dh)+1); 23 vector<unsigned char> ck(DH_size(dh)+1);
23 unsigned char *ckptr = &(ck.front())+1; 24 unsigned char *ckptr = &(ck.front())+1;
24 int cklen = DH_compute_key(ckptr,s_pub,dh); 25 int cklen = DH_compute_key(ckptr,s_pub,dh);
25 if(cklen<0) 26 if(cklen<0)
26 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); 27 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()");
27 if(cklen && (*ckptr)&0x80) { 28 if(cklen && (*ckptr)&0x80) {
28 (*(--ckptr))=0; ++cklen; } 29 (*(--ckptr))=0; ++cklen; }
29 assert(d_len<=SHA256_DIGEST_LENGTH); 30 assert(d_len<=SHA256_DIGEST_LENGTH);
30 unsigned char key_digest[SHA256_DIGEST_LENGTH]; 31 unsigned char key_digest[SHA256_DIGEST_LENGTH];
31 secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key")); 32 secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key"));
32 if(secret.size()!=exp_s_len) 33 if(secret.size()!=exp_s_len)
33 throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type"); 34 throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type");
34 }catch(opkele::failed_lookup& ofl) { 35 }catch(opkele::failed_lookup& ofl) {
35 throw bad_input(OPKELE_CP_ "Incoherent response from OP"); 36 throw bad_input(OPKELE_CP_ "Incoherent response from OP");
36 } OPKELE_RETHROW 37 } OPKELE_RETHROW
37 38
38 static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) { 39 static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) {
39 util::curl_pick_t curl = util::curl_pick_t::easy_init(); 40 util::curl_pick_t curl = util::curl_pick_t::easy_init();
40 if(!curl) 41 if(!curl)
41 throw exception_curl(OPKELE_CP_ "failed to initialize curl"); 42 throw exception_curl(OPKELE_CP_ "failed to initialize curl");
42 string request = inm.query_string(); 43 string request = inm.query_string();
43 CURLcode r; 44 CURLcode r;
44 (r=curl.misc_sets()) 45 (r=curl.misc_sets())
45 || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str())) 46 || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str()))
46 || (r=curl.easy_setopt(CURLOPT_POST,1)) 47 || (r=curl.easy_setopt(CURLOPT_POST,1))
47 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) 48 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data()))
48 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) 49 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length()))
49 || (r=curl.set_write()); 50 || (r=curl.set_write());
50 if(r) 51 if(r)
51 throw exception_curl(OPKELE_CP_ "failed to set curly options",r); 52 throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
52 if( (r=curl.easy_perform()) ) 53 if( (r=curl.easy_perform()) )
53 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); 54 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
54 oum.from_keyvalues(curl.response); 55 oum.from_keyvalues(curl.response);
55 } 56 }
56 57
57 58
58 assoc_t basic_RP::associate(const string& OP) { 59 assoc_t basic_RP::associate(const string& OP) {
59 util::dh_t dh = DH_new(); 60 util::dh_t dh = DH_new();
60 if(!dh) 61 if(!dh)
61 throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); 62 throw exception_openssl(OPKELE_CP_ "failed to DH_new()");
62 dh->p = util::dec_to_bignum(data::_default_p); 63 dh->p = util::dec_to_bignum(data::_default_p);
63 dh->g = util::dec_to_bignum(data::_default_g); 64 dh->g = util::dec_to_bignum(data::_default_g);
64 if(!DH_generate_key(dh)) 65 if(!DH_generate_key(dh))
65 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); 66 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()");
66 openid_message_t req; 67 openid_message_t req;
67 req.set_field("ns",OIURI_OPENID20); 68 req.set_field("ns",OIURI_OPENID20);
68 req.set_field("mode","associate"); 69 req.set_field("mode","associate");
69 req.set_field("dh_modulus",util::bignum_to_base64(dh->p)); 70 req.set_field("dh_modulus",util::bignum_to_base64(dh->p));
70 req.set_field("dh_gen",util::bignum_to_base64(dh->g)); 71 req.set_field("dh_gen",util::bignum_to_base64(dh->g));
71 req.set_field("dh_consumer_public",util::bignum_to_base64(dh->pub_key)); 72 req.set_field("dh_consumer_public",util::bignum_to_base64(dh->pub_key));
72 openid_message_t res; 73 openid_message_t res;
73 req.set_field("assoc_type","HMAC-SHA256"); 74 req.set_field("assoc_type","HMAC-SHA256");
74 req.set_field("session_type","DH-SHA256"); 75 req.set_field("session_type","DH-SHA256");
75 secret_t secret; 76 secret_t secret;
76 int expires_in; 77 int expires_in;
77 try { 78 try {
78 direct_request(res,req,OP); 79 direct_request(res,req,OP);
79 dh_get_secret( secret, res, 80 dh_get_secret( secret, res,
80 "HMAC-SHA256", "DH-SHA256", 81 "HMAC-SHA256", "DH-SHA256",
81 dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH ); 82 dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH );
82 expires_in = util::string_to_long(res.get_field("expires_in")); 83 expires_in = util::string_to_long(res.get_field("expires_in"));
83 }catch(exception&) { 84 }catch(exception&) {
84 try { 85 try {
85 req.set_field("assoc_type","HMAC-SHA1"); 86 req.set_field("assoc_type","HMAC-SHA1");
86 req.set_field("session_type","DH-SHA1"); 87 req.set_field("session_type","DH-SHA1");
87 direct_request(res,req,OP); 88 direct_request(res,req,OP);
88 dh_get_secret( secret, res, 89 dh_get_secret( secret, res,
89 "HMAC-SHA1", "DH-SHA1", 90 "HMAC-SHA1", "DH-SHA1",
90 dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH ); 91 dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH );
91 expires_in = util::string_to_long(res.get_field("expires_in")); 92 expires_in = util::string_to_long(res.get_field("expires_in"));
92 }catch(bad_input&) { 93 }catch(bad_input&) {
93 throw dumb_RP(OPKELE_CP_ "OP failed to supply an association"); 94 throw dumb_RP(OPKELE_CP_ "OP failed to supply an association");
94 } 95 }
95 } 96 }
96 return store_assoc( 97 return store_assoc(
97 OP, res.get_field("assoc_handle"), 98 OP, res.get_field("assoc_handle"),
98 res.get_field("assoc_type"), secret, 99 res.get_field("assoc_type"), secret,
99 expires_in ); 100 expires_in );
100 } 101 }
101 102
102 basic_openid_message& basic_RP::checkid_( 103 basic_openid_message& basic_RP::checkid_(
103 basic_openid_message& rv, 104 basic_openid_message& rv,
104 mode_t mode, 105 mode_t mode,
105 const string& return_to,const string& realm, 106 const string& return_to,const string& realm,
106 extension_t *ext) { 107 extension_t *ext) {
107 rv.reset_fields(); 108 rv.reset_fields();
108 rv.set_field("ns",OIURI_OPENID20); 109 rv.set_field("ns",OIURI_OPENID20);
109 if(mode==mode_checkid_immediate) 110 if(mode==mode_checkid_immediate)
110 rv.set_field("mode","checkid_immediate"); 111 rv.set_field("mode","checkid_immediate");
111 else if(mode==mode_checkid_setup) 112 else if(mode==mode_checkid_setup)
112 rv.set_field("mode","checkid_setup"); 113 rv.set_field("mode","checkid_setup");
113 else 114 else
114 throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); 115 throw bad_input(OPKELE_CP_ "unknown checkid_* mode");
115 if(realm.empty() && return_to.empty()) 116 if(realm.empty() && return_to.empty())
116 throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty"); 117 throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty");
117 if(!realm.empty()) { 118 if(!realm.empty()) {
118 rv.set_field("realm",realm); 119 rv.set_field("realm",realm);
119 rv.set_field("trust_root",realm); 120 rv.set_field("trust_root",realm);
120 } 121 }
121 if(!return_to.empty()) 122 if(!return_to.empty())
122 rv.set_field("return_to",return_to); 123 rv.set_field("return_to",return_to);
123 const openid_endpoint_t& ep = get_endpoint(); 124 const openid_endpoint_t& ep = get_endpoint();
124 rv.set_field("claimed_id",ep.claimed_id); 125 rv.set_field("claimed_id",ep.claimed_id);
125 rv.set_field("identity",ep.local_id); 126 rv.set_field("identity",ep.local_id);
126 try { 127 try {
127 rv.set_field("assoc_handle",find_assoc(ep.uri)->handle()); 128 rv.set_field("assoc_handle",find_assoc(ep.uri)->handle());
128 }catch(dumb_RP& drp) { 129 }catch(dumb_RP& drp) {
129 }catch(failed_lookup& fl) { 130 }catch(failed_lookup& fl) {
130 try { 131 try {
131 rv.set_field("assoc_handle",associate(ep.uri)->handle()); 132 rv.set_field("assoc_handle",associate(ep.uri)->handle());
132 }catch(dumb_RP& drp) { } 133 }catch(dumb_RP& drp) { }
133 } OPKELE_RETHROW 134 } OPKELE_RETHROW
134 if(ext) ext->rp_checkid_hook(rv); 135 if(ext) ext->rp_checkid_hook(rv);
135 return rv; 136 return rv;
136 } 137 }
137 138
138 class signed_part_message_proxy : public basic_openid_message { 139 class signed_part_message_proxy : public basic_openid_message {
139 public: 140 public:
140 const basic_openid_message& x; 141 const basic_openid_message& x;
141 set<string> signeds; 142 set<string> signeds;
142 143
143 signed_part_message_proxy(const basic_openid_message& xx) : x(xx) { 144 signed_part_message_proxy(const basic_openid_message& xx) : x(xx) {
144 const string& slist = x.get_field("signed"); 145 const string& slist = x.get_field("signed");
145 string::size_type p = 0; 146 string::size_type p = 0;
146 while(true) { 147 while(true) {
147 string::size_type co = slist.find(',',p); 148 string::size_type co = slist.find(',',p);
148 string f = (co==string::npos) 149 string f = (co==string::npos)
149 ?slist.substr(p):slist.substr(p,co-p); 150 ?slist.substr(p):slist.substr(p,co-p);
150 signeds.insert(f); 151 signeds.insert(f);
151 if(co==string::npos) break; 152 if(co==string::npos) break;
152 p = co+1; 153 p = co+1;
153 } 154 }
154 } 155 }
155 156
156 bool has_field(const string& n) const { 157 bool has_field(const string& n) const {
157 return signeds.find(n)!=signeds.end() && x.has_field(n); } 158 return signeds.find(n)!=signeds.end() && x.has_field(n); }
158 const string& get_field(const string& n) const { 159 const string& get_field(const string& n) const {
159 if(signeds.find(n)==signeds.end()) 160 if(signeds.find(n)==signeds.end())
160 throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed"); 161 throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed");
161 return x.get_field(n); } 162 return x.get_field(n); }
162 163
163 fields_iterator fields_begin() const { 164 fields_iterator fields_begin() const {
164 return signeds.begin(); } 165 return signeds.begin(); }
165 fields_iterator fields_end() const { 166 fields_iterator fields_end() const {
166 return signeds.end(); } 167 return signeds.end(); }
167 }; 168 };
168 169
169 static void parse_query(const string& u,string::size_type q, 170 static void parse_query(const string& u,string::size_type q,
170 map<string,string>& p) { 171 map<string,string>& p) {
171 if(q==string::npos) 172 if(q==string::npos)
172 return; 173 return;
173 assert(u[q]=='?'); 174 assert(u[q]=='?');
174 ++q; 175 ++q;
175 string::size_type l = u.size(); 176 string::size_type l = u.size();
176 while(q<l) { 177 while(q<l) {
177 string::size_type eq = u.find('=',q); 178 string::size_type eq = u.find('=',q);
178 string::size_type am = u.find('&',q); 179 string::size_type am = u.find('&',q);
179 if(am==string::npos) { 180 if(am==string::npos) {
180 if(eq==string::npos) { 181 if(eq==string::npos) {
181 p[""] = u.substr(q); 182 p[""] = u.substr(q);
182 }else{ 183 }else{
183 p[u.substr(q,eq-q)] = u.substr(eq+1); 184 p[u.substr(q,eq-q)] = u.substr(eq+1);
184 } 185 }
185 break; 186 break;
186 }else{ 187 }else{
187 if(eq==string::npos || eq>am) { 188 if(eq==string::npos || eq>am) {
188 p[""] = u.substr(q,eq-q); 189 p[""] = u.substr(q,eq-q);
189 }else{ 190 }else{
190 p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1); 191 p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1);
191 } 192 }
192 q = ++am; 193 q = ++am;
193 } 194 }
194 } 195 }
195 } 196 }
196 197
197 void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) { 198 void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) {
198 bool o2 = om.has_field("ns") 199 bool o2 = om.has_field("ns")
199 && om.get_field("ns")==OIURI_OPENID20; 200 && om.get_field("ns")==OIURI_OPENID20;
200 if( (!o2) && om.has_field("user_setup_url")) 201 if( (!o2) && om.has_field("user_setup_url"))