summaryrefslogtreecommitdiffabout
authorMichael Krelin <hacker@klever.net>2009-04-11 15:40:20 (UTC)
committer Michael Krelin <hacker@klever.net>2009-04-11 15:40:20 (UTC)
commit17de50174f73acefc99a181240481574431aa95a (patch) (unidiff)
treea8aa9cf124913acc37bc7954d35015f9fb59ebdd
parent381bfb49bfbfc569e6b5aa8e58a933de4397b053 (diff)
downloadlibopkele-17de50174f73acefc99a181240481574431aa95a.zip
libopkele-17de50174f73acefc99a181240481574431aa95a.tar.gz
libopkele-17de50174f73acefc99a181240481574431aa95a.tar.bz2
looks like yet another breakage by LJ
IIRC, previously, livejournal.com supplied empty op_endpoint URL. Now it doesn't supply it at all. Dunno which breakage is better. Signed-off-by: Michael Krelin <hacker@klever.net>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--lib/basic_rp.cc3
1 files changed, 2 insertions, 1 deletions
diff --git a/lib/basic_rp.cc b/lib/basic_rp.cc
index 8125aa7..9c7113b 100644
--- a/lib/basic_rp.cc
+++ b/lib/basic_rp.cc
@@ -93,234 +93,235 @@ namespace opkele {
93 openid_message_t res; 93 openid_message_t res;
94 req.set_field("assoc_type","HMAC-SHA256"); 94 req.set_field("assoc_type","HMAC-SHA256");
95 req.set_field("session_type","DH-SHA256"); 95 req.set_field("session_type","DH-SHA256");
96 secret_t secret; 96 secret_t secret;
97 int expires_in; 97 int expires_in;
98 try { 98 try {
99 direct_request(res,req,OP); 99 direct_request(res,req,OP);
100 dh_get_secret( secret, res, 100 dh_get_secret( secret, res,
101 "HMAC-SHA256", "DH-SHA256", 101 "HMAC-SHA256", "DH-SHA256",
102 dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH ); 102 dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH );
103 expires_in = util::string_to_long(res.get_field("expires_in")); 103 expires_in = util::string_to_long(res.get_field("expires_in"));
104 }catch(exception&) { 104 }catch(exception&) {
105 try { 105 try {
106 req.set_field("assoc_type","HMAC-SHA1"); 106 req.set_field("assoc_type","HMAC-SHA1");
107 req.set_field("session_type","DH-SHA1"); 107 req.set_field("session_type","DH-SHA1");
108 direct_request(res,req,OP); 108 direct_request(res,req,OP);
109 dh_get_secret( secret, res, 109 dh_get_secret( secret, res,
110 "HMAC-SHA1", "DH-SHA1", 110 "HMAC-SHA1", "DH-SHA1",
111 dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH ); 111 dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH );
112 expires_in = util::string_to_long(res.get_field("expires_in")); 112 expires_in = util::string_to_long(res.get_field("expires_in"));
113 }catch(bad_input&) { 113 }catch(bad_input&) {
114 throw dumb_RP(OPKELE_CP_ "OP failed to supply an association"); 114 throw dumb_RP(OPKELE_CP_ "OP failed to supply an association");
115 } 115 }
116 } 116 }
117 return store_assoc( 117 return store_assoc(
118 OP, res.get_field("assoc_handle"), 118 OP, res.get_field("assoc_handle"),
119 res.get_field("assoc_type"), secret, 119 res.get_field("assoc_type"), secret,
120 expires_in ); 120 expires_in );
121 } 121 }
122 122
123 basic_openid_message& basic_RP::checkid_( 123 basic_openid_message& basic_RP::checkid_(
124 basic_openid_message& rv, 124 basic_openid_message& rv,
125 mode_t mode, 125 mode_t mode,
126 const string& return_to,const string& realm, 126 const string& return_to,const string& realm,
127 extension_t *ext) { 127 extension_t *ext) {
128 rv.reset_fields(); 128 rv.reset_fields();
129 rv.set_field("ns",OIURI_OPENID20); 129 rv.set_field("ns",OIURI_OPENID20);
130 if(mode==mode_checkid_immediate) 130 if(mode==mode_checkid_immediate)
131 rv.set_field("mode","checkid_immediate"); 131 rv.set_field("mode","checkid_immediate");
132 else if(mode==mode_checkid_setup) 132 else if(mode==mode_checkid_setup)
133 rv.set_field("mode","checkid_setup"); 133 rv.set_field("mode","checkid_setup");
134 else 134 else
135 throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); 135 throw bad_input(OPKELE_CP_ "unknown checkid_* mode");
136 if(realm.empty() && return_to.empty()) 136 if(realm.empty() && return_to.empty())
137 throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty"); 137 throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty");
138 if(!realm.empty()) { 138 if(!realm.empty()) {
139 rv.set_field("realm",realm); 139 rv.set_field("realm",realm);
140 rv.set_field("trust_root",realm); 140 rv.set_field("trust_root",realm);
141 } 141 }
142 if(!return_to.empty()) 142 if(!return_to.empty())
143 rv.set_field("return_to",return_to); 143 rv.set_field("return_to",return_to);
144 const openid_endpoint_t& ep = get_endpoint(); 144 const openid_endpoint_t& ep = get_endpoint();
145 rv.set_field("claimed_id",ep.claimed_id); 145 rv.set_field("claimed_id",ep.claimed_id);
146 rv.set_field("identity",ep.local_id); 146 rv.set_field("identity",ep.local_id);
147 try { 147 try {
148 rv.set_field("assoc_handle",find_assoc(ep.uri)->handle()); 148 rv.set_field("assoc_handle",find_assoc(ep.uri)->handle());
149 }catch(dumb_RP& drp) { 149 }catch(dumb_RP& drp) {
150 }catch(failed_lookup& fl) { 150 }catch(failed_lookup& fl) {
151 try { 151 try {
152 rv.set_field("assoc_handle",associate(ep.uri)->handle()); 152 rv.set_field("assoc_handle",associate(ep.uri)->handle());
153 }catch(dumb_RP& drp) { } 153 }catch(dumb_RP& drp) { }
154 } OPKELE_RETHROW 154 } OPKELE_RETHROW
155 if(ext) ext->rp_checkid_hook(rv); 155 if(ext) ext->rp_checkid_hook(rv);
156 return rv; 156 return rv;
157 } 157 }
158 158
159 class signed_part_message_proxy : public basic_openid_message { 159 class signed_part_message_proxy : public basic_openid_message {
160 public: 160 public:
161 const basic_openid_message& x; 161 const basic_openid_message& x;
162 set<string> signeds; 162 set<string> signeds;
163 163
164 signed_part_message_proxy(const basic_openid_message& xx) : x(xx) { 164 signed_part_message_proxy(const basic_openid_message& xx) : x(xx) {
165 const string& slist = x.get_field("signed"); 165 const string& slist = x.get_field("signed");
166 string::size_type p = 0; 166 string::size_type p = 0;
167 while(true) { 167 while(true) {
168 string::size_type co = slist.find(',',p); 168 string::size_type co = slist.find(',',p);
169 string f = (co==string::npos) 169 string f = (co==string::npos)
170 ?slist.substr(p):slist.substr(p,co-p); 170 ?slist.substr(p):slist.substr(p,co-p);
171 signeds.insert(f); 171 signeds.insert(f);
172 if(co==string::npos) break; 172 if(co==string::npos) break;
173 p = co+1; 173 p = co+1;
174 } 174 }
175 } 175 }
176 176
177 bool has_field(const string& n) const { 177 bool has_field(const string& n) const {
178 return signeds.find(n)!=signeds.end() && x.has_field(n); } 178 return signeds.find(n)!=signeds.end() && x.has_field(n); }
179 const string& get_field(const string& n) const { 179 const string& get_field(const string& n) const {
180 if(signeds.find(n)==signeds.end()) 180 if(signeds.find(n)==signeds.end())
181 throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed"); 181 throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed");
182 return x.get_field(n); } 182 return x.get_field(n); }
183 183
184 fields_iterator fields_begin() const { 184 fields_iterator fields_begin() const {
185 return signeds.begin(); } 185 return signeds.begin(); }
186 fields_iterator fields_end() const { 186 fields_iterator fields_end() const {
187 return signeds.end(); } 187 return signeds.end(); }
188 }; 188 };
189 189
190 static void parse_query(const string& u,string::size_type q, 190 static void parse_query(const string& u,string::size_type q,
191 map<string,string>& p) { 191 map<string,string>& p) {
192 if(q==string::npos) 192 if(q==string::npos)
193 return; 193 return;
194 assert(u[q]=='?'); 194 assert(u[q]=='?');
195 ++q; 195 ++q;
196 string::size_type l = u.size(); 196 string::size_type l = u.size();
197 while(q<l) { 197 while(q<l) {
198 string::size_type eq = u.find('=',q); 198 string::size_type eq = u.find('=',q);
199 string::size_type am = u.find('&',q); 199 string::size_type am = u.find('&',q);
200 if(am==string::npos) { 200 if(am==string::npos) {
201 if(eq==string::npos) { 201 if(eq==string::npos) {
202 p[""] = u.substr(q); 202 p[""] = u.substr(q);
203 }else{ 203 }else{
204 p[u.substr(q,eq-q)] = u.substr(eq+1); 204 p[u.substr(q,eq-q)] = u.substr(eq+1);
205 } 205 }
206 break; 206 break;
207 }else{ 207 }else{
208 if(eq==string::npos || eq>am) { 208 if(eq==string::npos || eq>am) {
209 p[""] = u.substr(q,eq-q); 209 p[""] = u.substr(q,eq-q);
210 }else{ 210 }else{
211 p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1); 211 p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1);
212 } 212 }
213 q = ++am; 213 q = ++am;
214 } 214 }
215 } 215 }
216 } 216 }
217 217
218 void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) { 218 void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) {
219 reset_vars(); 219 reset_vars();
220 bool o2 = om.has_field("ns") 220 bool o2 = om.has_field("ns")
221 && om.get_field("ns")==OIURI_OPENID20 && !om.get_field("op_endpoint").empty(); 221 && om.get_field("ns")==OIURI_OPENID20
222 && om.has_field("op_endpoint") && !om.get_field("op_endpoint").empty();
222 if( (!o2) && om.has_field("user_setup_url")) 223 if( (!o2) && om.has_field("user_setup_url"))
223 throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided", 224 throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",
224 om.get_field("user_setup_url")); 225 om.get_field("user_setup_url"));
225 string m = om.get_field("mode"); 226 string m = om.get_field("mode");
226 if(o2 && m=="setup_needed") 227 if(o2 && m=="setup_needed")
227 throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided"); 228 throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided");
228 if(m=="cancel") 229 if(m=="cancel")
229 throw id_res_cancel(OPKELE_CP_ "authentication cancelled"); 230 throw id_res_cancel(OPKELE_CP_ "authentication cancelled");
230 bool go_dumb=false; 231 bool go_dumb=false;
231 try { 232 try {
232 string OP = o2 233 string OP = o2
233 ?om.get_field("op_endpoint") 234 ?om.get_field("op_endpoint")
234 :get_endpoint().uri; 235 :get_endpoint().uri;
235 assoc_t assoc = retrieve_assoc( 236 assoc_t assoc = retrieve_assoc(
236 OP,om.get_field("assoc_handle")); 237 OP,om.get_field("assoc_handle"));
237 if(om.get_field("sig")!=util::base64_signature(assoc,om)) 238 if(om.get_field("sig")!=util::base64_signature(assoc,om))
238 throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); 239 throw id_res_mismatch(OPKELE_CP_ "signature mismatch");
239 }catch(dumb_RP& drp) { 240 }catch(dumb_RP& drp) {
240 go_dumb=true; 241 go_dumb=true;
241 }catch(failed_lookup& e) { 242 }catch(failed_lookup& e) {
242 go_dumb=true; 243 go_dumb=true;
243 } OPKELE_RETHROW 244 } OPKELE_RETHROW
244 if(go_dumb) { 245 if(go_dumb) {
245 try { 246 try {
246 string OP = o2 247 string OP = o2
247 ?om.get_field("op_endpoint") 248 ?om.get_field("op_endpoint")
248 :get_endpoint().uri; 249 :get_endpoint().uri;
249 check_authentication(OP,om); 250 check_authentication(OP,om);
250 }catch(failed_check_authentication& fca) { 251 }catch(failed_check_authentication& fca) {
251 throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); 252 throw id_res_failed(OPKELE_CP_ "failed to check_authentication()");
252 } OPKELE_RETHROW 253 } OPKELE_RETHROW
253 } 254 }
254 signed_part_message_proxy signeds(om); 255 signed_part_message_proxy signeds(om);
255 if(o2) { 256 if(o2) {
256 check_nonce(om.get_field("op_endpoint"), 257 check_nonce(om.get_field("op_endpoint"),
257 om.get_field("response_nonce")); 258 om.get_field("response_nonce"));
258 static const char *mustsign[] = { 259 static const char *mustsign[] = {
259 "op_endpoint", "return_to", "response_nonce", "assoc_handle", 260 "op_endpoint", "return_to", "response_nonce", "assoc_handle",
260 "claimed_id", "identity" }; 261 "claimed_id", "identity" };
261 for(size_t ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) { 262 for(size_t ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) {
262 if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms])) 263 if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms]))
263 throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs"); 264 throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs");
264 } 265 }
265 if( ( 266 if( (
266 (om.has_field("claimed_id")?1:0) 267 (om.has_field("claimed_id")?1:0)
267 ^ 268 ^
268 (om.has_field("identity")?1:0) 269 (om.has_field("identity")?1:0)
269 )&1 ) 270 )&1 )
270 throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent"); 271 throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent");
271 272
272 string turl = util::rfc_3986_normalize_uri(get_this_url()); 273 string turl = util::rfc_3986_normalize_uri(get_this_url());
273 util::strip_uri_fragment_part(turl); 274 util::strip_uri_fragment_part(turl);
274 string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to")); 275 string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to"));
275 util::strip_uri_fragment_part(rurl); 276 util::strip_uri_fragment_part(rurl);
276 string::size_type 277 string::size_type
277 tq = turl.find('?'), rq = rurl.find('?'); 278 tq = turl.find('?'), rq = rurl.find('?');
278 if( 279 if(
279 ((tq==string::npos)?turl:turl.substr(0,tq)) 280 ((tq==string::npos)?turl:turl.substr(0,tq))
280 != 281 !=
281 ((rq==string::npos)?rurl:rurl.substr(0,rq)) 282 ((rq==string::npos)?rurl:rurl.substr(0,rq))
282 ) 283 )
283 throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url"); 284 throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url");
284 map<string,string> tp; parse_query(turl,tq,tp); 285 map<string,string> tp; parse_query(turl,tq,tp);
285 map<string,string> rp; parse_query(rurl,rq,rp); 286 map<string,string> rp; parse_query(rurl,rq,rp);
286 for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) { 287 for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) {
287 map<string,string>::const_iterator tpi = tp.find(rpi->first); 288 map<string,string>::const_iterator tpi = tp.find(rpi->first);
288 if(tpi==tp.end()) 289 if(tpi==tp.end())
289 throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request"); 290 throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request");
290 if(tpi->second!=rpi->second) 291 if(tpi->second!=rpi->second)
291 throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request"); 292 throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request");
292 } 293 }
293 294
294 if(om.has_field("claimed_id")) { 295 if(om.has_field("claimed_id")) {
295 claimed_id = om.get_field("claimed_id"); 296 claimed_id = om.get_field("claimed_id");
296 identity = om.get_field("identity"); 297 identity = om.get_field("identity");
297 verify_OP( 298 verify_OP(
298 om.get_field("op_endpoint"), 299 om.get_field("op_endpoint"),
299 claimed_id, identity ); 300 claimed_id, identity );
300 } 301 }
301 302
302 }else{ 303 }else{
303 claimed_id = get_endpoint().claimed_id; 304 claimed_id = get_endpoint().claimed_id;
304 /* TODO: check if this is the identity we asked for */ 305 /* TODO: check if this is the identity we asked for */
305 identity = om.get_field("identity"); 306 identity = om.get_field("identity");
306 } 307 }
307 if(ext) ext->rp_id_res_hook(om,signeds); 308 if(ext) ext->rp_id_res_hook(om,signeds);
308 } 309 }
309 310
310 void basic_RP::check_authentication(const string& OP, 311 void basic_RP::check_authentication(const string& OP,
311 const basic_openid_message& om){ 312 const basic_openid_message& om){
312 openid_message_t res; 313 openid_message_t res;
313 static const string checkauthmode = "check_authentication"; 314 static const string checkauthmode = "check_authentication";
314 direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP); 315 direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP);
315 if(res.has_field("is_valid")) { 316 if(res.has_field("is_valid")) {
316 if(res.get_field("is_valid")=="true") { 317 if(res.get_field("is_valid")=="true") {
317 if(res.has_field("invalidate_handle")) 318 if(res.has_field("invalidate_handle"))
318 invalidate_assoc(OP,res.get_field("invalidate_handle")); 319 invalidate_assoc(OP,res.get_field("invalidate_handle"));
319 return; 320 return;
320 } 321 }
321 } 322 }
322 throw failed_check_authentication( 323 throw failed_check_authentication(
323 OPKELE_CP_ "failed to verify response"); 324 OPKELE_CP_ "failed to verify response");
324 } 325 }
325 326
326} 327}