summaryrefslogtreecommitdiffabout
path: root/lib
Unidiff
Diffstat (limited to 'lib') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/basic_op.cc5
-rw-r--r--lib/basic_rp.cc4
-rw-r--r--lib/extension.cc25
-rw-r--r--lib/extension_chain.cc27
-rw-r--r--lib/sreg.cc28
5 files changed, 67 insertions, 22 deletions
diff --git a/lib/basic_op.cc b/lib/basic_op.cc
index c89d1d7..9e2ea5a 100644
--- a/lib/basic_op.cc
+++ b/lib/basic_op.cc
@@ -68,261 +68,264 @@ namespace opkele {
68 size_t d_len = 0; 68 size_t d_len = 0;
69 enum { 69 enum {
70 sess_cleartext, sess_dh_sha1, sess_dh_sha256 70 sess_cleartext, sess_dh_sha1, sess_dh_sha256
71 } st = sess_cleartext; 71 } st = sess_cleartext;
72 string sts = inm.get_field("session_type"); 72 string sts = inm.get_field("session_type");
73 string ats = inm.get_field("assoc_type"); 73 string ats = inm.get_field("assoc_type");
74 if(sts=="DH-SHA1" || sts=="DH-SHA256") { 74 if(sts=="DH-SHA1" || sts=="DH-SHA256") {
75 if(!(dh = DH_new())) 75 if(!(dh = DH_new()))
76 throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); 76 throw exception_openssl(OPKELE_CP_ "failed to DH_new()");
77 c_pub = util::base64_to_bignum(inm.get_field("dh_consumer_public")); 77 c_pub = util::base64_to_bignum(inm.get_field("dh_consumer_public"));
78 try { dh->p = util::base64_to_bignum(inm.get_field("dh_modulus")); 78 try { dh->p = util::base64_to_bignum(inm.get_field("dh_modulus"));
79 }catch(failed_lookup&) { 79 }catch(failed_lookup&) {
80 dh->p = util::dec_to_bignum(data::_default_p); } 80 dh->p = util::dec_to_bignum(data::_default_p); }
81 try { dh->g = util::base64_to_bignum(inm.get_field("dh_gen")); 81 try { dh->g = util::base64_to_bignum(inm.get_field("dh_gen"));
82 }catch(failed_lookup&) { 82 }catch(failed_lookup&) {
83 dh->g = util::dec_to_bignum(data::_default_g); } 83 dh->g = util::dec_to_bignum(data::_default_g); }
84 if(!DH_generate_key(dh)) 84 if(!DH_generate_key(dh))
85 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); 85 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()");
86 vector<unsigned char> ck(DH_size(dh)+1); 86 vector<unsigned char> ck(DH_size(dh)+1);
87 unsigned char *ckptr = &(ck.front())+1; 87 unsigned char *ckptr = &(ck.front())+1;
88 int cklen = DH_compute_key(ckptr,c_pub,dh); 88 int cklen = DH_compute_key(ckptr,c_pub,dh);
89 if(cklen<0) 89 if(cklen<0)
90 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); 90 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()");
91 if(cklen && (*ckptr)&0x80) { 91 if(cklen && (*ckptr)&0x80) {
92 (*(--ckptr)) = 0; ++cklen; } 92 (*(--ckptr)) = 0; ++cklen; }
93 if(sts=="DH-SHA1") { 93 if(sts=="DH-SHA1") {
94 SHA1(ckptr,cklen,key_digest); d_len = SHA_DIGEST_LENGTH; 94 SHA1(ckptr,cklen,key_digest); d_len = SHA_DIGEST_LENGTH;
95 }else if(sts=="DH-SHA256") { 95 }else if(sts=="DH-SHA256") {
96 SHA256(ckptr,cklen,key_digest); d_len = SHA256_DIGEST_LENGTH; 96 SHA256(ckptr,cklen,key_digest); d_len = SHA256_DIGEST_LENGTH;
97 }else 97 }else
98 throw internal_error(OPKELE_CP_ "I thought I knew the session type"); 98 throw internal_error(OPKELE_CP_ "I thought I knew the session type");
99 }else 99 }else
100 throw unsupported(OPKELE_CP_ "Unsupported session_type"); 100 throw unsupported(OPKELE_CP_ "Unsupported session_type");
101 assoc_t assoc; 101 assoc_t assoc;
102 if(ats=="HMAC-SHA1") 102 if(ats=="HMAC-SHA1")
103 assoc = alloc_assoc(ats,SHA_DIGEST_LENGTH,true); 103 assoc = alloc_assoc(ats,SHA_DIGEST_LENGTH,true);
104 else if(ats=="HMAC-SHA256") 104 else if(ats=="HMAC-SHA256")
105 assoc = alloc_assoc(ats,SHA256_DIGEST_LENGTH,true); 105 assoc = alloc_assoc(ats,SHA256_DIGEST_LENGTH,true);
106 else 106 else
107 throw unsupported(OPKELE_CP_ "Unsupported assoc_type"); 107 throw unsupported(OPKELE_CP_ "Unsupported assoc_type");
108 oum.reset_fields(); 108 oum.reset_fields();
109 oum.set_field("ns",OIURI_OPENID20); 109 oum.set_field("ns",OIURI_OPENID20);
110 oum.set_field("assoc_type",assoc->assoc_type()); 110 oum.set_field("assoc_type",assoc->assoc_type());
111 oum.set_field("assoc_handle",assoc->handle()); 111 oum.set_field("assoc_handle",assoc->handle());
112 oum.set_field("expires_in",util::long_to_string(assoc->expires_in())); 112 oum.set_field("expires_in",util::long_to_string(assoc->expires_in()));
113 secret_t secret = assoc->secret(); 113 secret_t secret = assoc->secret();
114 if(sts=="DH-SHA1" || sts=="DH-SHA256") { 114 if(sts=="DH-SHA1" || sts=="DH-SHA256") {
115 if(d_len != secret.size()) 115 if(d_len != secret.size())
116 throw bad_input(OPKELE_CP_ "Association secret and session MAC are not of the same size"); 116 throw bad_input(OPKELE_CP_ "Association secret and session MAC are not of the same size");
117 oum.set_field("session_type",sts); 117 oum.set_field("session_type",sts);
118 oum.set_field("dh_server_public",util::bignum_to_base64(dh->pub_key)); 118 oum.set_field("dh_server_public",util::bignum_to_base64(dh->pub_key));
119 string b64; secret.enxor_to_base64(key_digest,b64); 119 string b64; secret.enxor_to_base64(key_digest,b64);
120 oum.set_field("enc_mac_key",b64); 120 oum.set_field("enc_mac_key",b64);
121 }else /* TODO: support cleartext over encrypted connection */ 121 }else /* TODO: support cleartext over encrypted connection */
122 throw unsupported(OPKELE_CP_ "Unsupported session type"); 122 throw unsupported(OPKELE_CP_ "Unsupported session type");
123 return oum; 123 return oum;
124 } catch(unsupported& u) { 124 } catch(unsupported& u) {
125 oum.reset_fields(); 125 oum.reset_fields();
126 oum.set_field("ns",OIURI_OPENID20); 126 oum.set_field("ns",OIURI_OPENID20);
127 oum.set_field("error",u.what()); 127 oum.set_field("error",u.what());
128 oum.set_field("error_code","unsupported-type"); 128 oum.set_field("error_code","unsupported-type");
129 oum.set_field("session_type","DH-SHA256"); 129 oum.set_field("session_type","DH-SHA256");
130 oum.set_field("assoc_type","HMAC-SHA256"); 130 oum.set_field("assoc_type","HMAC-SHA256");
131 return oum; 131 return oum;
132 } 132 }
133 133
134 void basic_op::checkid_(const basic_openid_message& inm, 134 void basic_op::checkid_(const basic_openid_message& inm,
135 extension_t *ext) { 135 extension_t *ext) {
136 reset_vars(); 136 reset_vars();
137 string mode = inm.get_field("mode"); 137 string mode = inm.get_field("mode");
138 if(mode=="checkid_setup") 138 if(mode=="checkid_setup")
139 mode = mode_checkid_setup; 139 mode = mode_checkid_setup;
140 else if(mode=="checkid_immediate") 140 else if(mode=="checkid_immediate")
141 mode = mode_checkid_immediate; 141 mode = mode_checkid_immediate;
142 else 142 else
143 throw bad_input(OPKELE_CP_ "Invalid checkid_* mode"); 143 throw bad_input(OPKELE_CP_ "Invalid checkid_* mode");
144 try { 144 try {
145 assoc = retrieve_assoc(invalidate_handle=inm.get_field("assoc_handle")); 145 assoc = retrieve_assoc(invalidate_handle=inm.get_field("assoc_handle"));
146 invalidate_handle.clear(); 146 invalidate_handle.clear();
147 }catch(failed_lookup&) { 147 }catch(failed_lookup&) {
148 // no handle specified or no valid assoc found, go dumb 148 // no handle specified or no valid assoc found, go dumb
149 assoc = alloc_assoc("HMAC-SHA256",SHA256_DIGEST_LENGTH,true); 149 assoc = alloc_assoc("HMAC-SHA256",SHA256_DIGEST_LENGTH,true);
150 } 150 }
151 try { 151 try {
152 openid2 = (inm.get_field("ns")==OIURI_OPENID20); 152 openid2 = (inm.get_field("ns")==OIURI_OPENID20);
153 }catch(failed_lookup&) { openid2 = false; } 153 }catch(failed_lookup&) { openid2 = false; }
154 try { 154 try {
155 return_to = inm.get_field("return_to"); 155 return_to = inm.get_field("return_to");
156 }catch(failed_lookup&) { } 156 }catch(failed_lookup&) { }
157 if(openid2) { 157 if(openid2) {
158 try { 158 try {
159 realm = inm.get_field("realm"); 159 realm = inm.get_field("realm");
160 }catch(failed_lookup&) { 160 }catch(failed_lookup&) {
161 try { 161 try {
162 realm = inm.get_field("trust_root"); 162 realm = inm.get_field("trust_root");
163 }catch(failed_lookup&) { 163 }catch(failed_lookup&) {
164 if(return_to.empty()) 164 if(return_to.empty())
165 throw bad_input(OPKELE_CP_ 165 throw bad_input(OPKELE_CP_
166 "Both realm and return_to are unset"); 166 "Both realm and return_to are unset");
167 realm = return_to; 167 realm = return_to;
168 } 168 }
169 } 169 }
170 }else{ 170 }else{
171 try { 171 try {
172 realm = inm.get_field("trust_root"); 172 realm = inm.get_field("trust_root");
173 }catch(failed_lookup&) { 173 }catch(failed_lookup&) {
174 if(return_to.empty()) 174 if(return_to.empty())
175 throw bad_input(OPKELE_CP_ 175 throw bad_input(OPKELE_CP_
176 "Both realm and return_to are unset"); 176 "Both realm and return_to are unset");
177 realm = return_to; 177 realm = return_to;
178 } 178 }
179 } 179 }
180 try { 180 try {
181 identity = inm.get_field("identity"); 181 identity = inm.get_field("identity");
182 try { 182 try {
183 claimed_id = inm.get_field("claimed_id"); 183 claimed_id = inm.get_field("claimed_id");
184 }catch(failed_lookup&) { 184 }catch(failed_lookup&) {
185 if(openid2) 185 if(openid2)
186 throw bad_input(OPKELE_CP_ 186 throw bad_input(OPKELE_CP_
187 "claimed_id and identity must be either both present or both absent"); 187 "claimed_id and identity must be either both present or both absent");
188 claimed_id = identity; 188 claimed_id = identity;
189 } 189 }
190 }catch(failed_lookup&) { 190 }catch(failed_lookup&) {
191 if(openid2 && inm.has_field("claimed_id")) 191 if(openid2 && inm.has_field("claimed_id"))
192 throw bad_input(OPKELE_CP_ 192 throw bad_input(OPKELE_CP_
193 "claimed_id and identity must be either both present or both absent"); 193 "claimed_id and identity must be either both present or both absent");
194 } 194 }
195 verify_return_to(); 195 verify_return_to();
196 if(ext) ext->op_checkid_hook(inm);
196 } 197 }
197 198
198 basic_openid_message& basic_op::id_res(basic_openid_message& om) { 199 basic_openid_message& basic_op::id_res(basic_openid_message& om,
200 extension_t *ext) {
199 assert(assoc); 201 assert(assoc);
200 assert(!return_to.empty()); 202 assert(!return_to.empty());
201 assert(!is_id_select()); 203 assert(!is_id_select());
202 time_t now = time(0); 204 time_t now = time(0);
203 struct tm gmt; gmtime_r(&now,&gmt); 205 struct tm gmt; gmtime_r(&now,&gmt);
204 char w3timestr[24]; 206 char w3timestr[24];
205 if(!strftime(w3timestr,sizeof(w3timestr),"%Y-%m-%dT%H:%M:%SZ",&gmt)) 207 if(!strftime(w3timestr,sizeof(w3timestr),"%Y-%m-%dT%H:%M:%SZ",&gmt))
206 throw failed_conversion(OPKELE_CP_ 208 throw failed_conversion(OPKELE_CP_
207 "Failed to build time string for nonce" ); 209 "Failed to build time string for nonce" );
208 om.set_field("ns",OIURI_OPENID20); 210 om.set_field("ns",OIURI_OPENID20);
209 om.set_field("mode","id_res"); 211 om.set_field("mode","id_res");
210 om.set_field("op_endpoint",get_op_endpoint()); 212 om.set_field("op_endpoint",get_op_endpoint());
211 string ats = "ns,mode,op_endpoint,return_to,response_nonce," 213 string ats = "ns,mode,op_endpoint,return_to,response_nonce,"
212 "assoc_handle,signed"; 214 "assoc_handle,signed";
213 if(!identity.empty()) { 215 if(!identity.empty()) {
214 om.set_field("identity",identity); 216 om.set_field("identity",identity);
215 om.set_field("claimed_id",claimed_id); 217 om.set_field("claimed_id",claimed_id);
216 ats += ",identity,claimed_id"; 218 ats += ",identity,claimed_id";
217 } 219 }
218 om.set_field("return_to",return_to); 220 om.set_field("return_to",return_to);
219 string nonce = w3timestr; 221 string nonce = w3timestr;
220 om.set_field("response_nonce",alloc_nonce(nonce,assoc->stateless())); 222 om.set_field("response_nonce",alloc_nonce(nonce,assoc->stateless()));
221 if(!invalidate_handle.empty()) { 223 if(!invalidate_handle.empty()) {
222 om.set_field("invalidate_handle",invalidate_handle); 224 om.set_field("invalidate_handle",invalidate_handle);
223 ats += ",invalidate_handle"; 225 ats += ",invalidate_handle";
224 } 226 }
225 om.set_field("assoc_handle",assoc->handle()); 227 om.set_field("assoc_handle",assoc->handle());
226 om.add_to_signed(ats); 228 om.add_to_signed(ats);
229 if(ext) ext->op_id_res_hook(om);
227 om.set_field("sig",util::base64_signature(assoc,om)); 230 om.set_field("sig",util::base64_signature(assoc,om));
228 return om; 231 return om;
229 } 232 }
230 233
231 basic_openid_message& basic_op::cancel(basic_openid_message& om) { 234 basic_openid_message& basic_op::cancel(basic_openid_message& om) {
232 assert(!return_to.empty()); 235 assert(!return_to.empty());
233 om.set_field("ns",OIURI_OPENID20); 236 om.set_field("ns",OIURI_OPENID20);
234 om.set_field("mode","cancel"); 237 om.set_field("mode","cancel");
235 return om; 238 return om;
236 } 239 }
237 240
238 basic_openid_message& basic_op::error(basic_openid_message& om, 241 basic_openid_message& basic_op::error(basic_openid_message& om,
239 const string& error,const string& contact, 242 const string& error,const string& contact,
240 const string& reference ) { 243 const string& reference ) {
241 assert(!return_to.empty()); 244 assert(!return_to.empty());
242 om.set_field("ns",OIURI_OPENID20); 245 om.set_field("ns",OIURI_OPENID20);
243 om.set_field("mode","error"); 246 om.set_field("mode","error");
244 om.set_field("error",error); 247 om.set_field("error",error);
245 om.set_field("contact",contact); 248 om.set_field("contact",contact);
246 om.set_field("reference",reference); 249 om.set_field("reference",reference);
247 return om; 250 return om;
248 } 251 }
249 252
250 basic_openid_message& basic_op::setup_needed( 253 basic_openid_message& basic_op::setup_needed(
251 basic_openid_message& oum,const basic_openid_message& inm) { 254 basic_openid_message& oum,const basic_openid_message& inm) {
252 assert(mode==mode_checkid_immediate); 255 assert(mode==mode_checkid_immediate);
253 assert(!return_to.empty()); 256 assert(!return_to.empty());
254 if(openid2) { 257 if(openid2) {
255 oum.set_field("ns",OIURI_OPENID20); 258 oum.set_field("ns",OIURI_OPENID20);
256 oum.set_field("mode","setup_needed"); 259 oum.set_field("mode","setup_needed");
257 }else{ 260 }else{
258 oum.set_field("mode","id_res"); 261 oum.set_field("mode","id_res");
259 static const string setupmode = "checkid_setup"; 262 static const string setupmode = "checkid_setup";
260 oum.set_field("user_setup_url", 263 oum.set_field("user_setup_url",
261 util::change_mode_message_proxy(inm,setupmode) 264 util::change_mode_message_proxy(inm,setupmode)
262 .append_query(get_op_endpoint())); 265 .append_query(get_op_endpoint()));
263 } 266 }
264 return oum; 267 return oum;
265 } 268 }
266 269
267 basic_openid_message& basic_op::check_authentication( 270 basic_openid_message& basic_op::check_authentication(
268 basic_openid_message& oum, 271 basic_openid_message& oum,
269 const basic_openid_message& inm) try { 272 const basic_openid_message& inm) try {
270 assert(inm.get_field("mode")=="check_authentication"); 273 assert(inm.get_field("mode")=="check_authentication");
271 oum.reset_fields(); 274 oum.reset_fields();
272 oum.set_field("ns",OIURI_OPENID20); 275 oum.set_field("ns",OIURI_OPENID20);
273 bool o2; 276 bool o2;
274 try { 277 try {
275 o2 = (inm.get_field("ns")==OIURI_OPENID20); 278 o2 = (inm.get_field("ns")==OIURI_OPENID20);
276 }catch(failed_lookup&) { o2 = false; } 279 }catch(failed_lookup&) { o2 = false; }
277 string nonce; 280 string nonce;
278 if(o2) { 281 if(o2) {
279 try { 282 try {
280 if(!check_nonce(nonce = inm.get_field("response_nonce"))) 283 if(!check_nonce(nonce = inm.get_field("response_nonce")))
281 throw failed_check_authentication(OPKELE_CP_ "Invalid nonce"); 284 throw failed_check_authentication(OPKELE_CP_ "Invalid nonce");
282 }catch(failed_lookup&) { 285 }catch(failed_lookup&) {
283 throw failed_check_authentication(OPKELE_CP_ "No nonce provided with check_authentication request"); 286 throw failed_check_authentication(OPKELE_CP_ "No nonce provided with check_authentication request");
284 } 287 }
285 } 288 }
286 try { 289 try {
287 assoc = retrieve_assoc(inm.get_field("assoc_handle")); 290 assoc = retrieve_assoc(inm.get_field("assoc_handle"));
288 if(!assoc->stateless()) 291 if(!assoc->stateless())
289 throw failed_check_authentication(OPKELE_CP_ "Will not do check_authentication on a stateful handle"); 292 throw failed_check_authentication(OPKELE_CP_ "Will not do check_authentication on a stateful handle");
290 }catch(failed_lookup&) { 293 }catch(failed_lookup&) {
291 throw failed_check_authentication(OPKELE_CP_ "No assoc_handle or invalid assoc_handle specified with check_authentication request"); 294 throw failed_check_authentication(OPKELE_CP_ "No assoc_handle or invalid assoc_handle specified with check_authentication request");
292 } 295 }
293 static const string idresmode = "id_res"; 296 static const string idresmode = "id_res";
294 try { 297 try {
295 if(util::base64_signature(assoc,util::change_mode_message_proxy(inm,idresmode))!=inm.get_field("sig")) 298 if(util::base64_signature(assoc,util::change_mode_message_proxy(inm,idresmode))!=inm.get_field("sig"))
296 throw failed_check_authentication(OPKELE_CP_ "Signature mismatch"); 299 throw failed_check_authentication(OPKELE_CP_ "Signature mismatch");
297 }catch(failed_lookup&) { 300 }catch(failed_lookup&) {
298 throw failed_check_authentication(OPKELE_CP_ "failed to calculate signature"); 301 throw failed_check_authentication(OPKELE_CP_ "failed to calculate signature");
299 } 302 }
300 oum.set_field("is_valid","true"); 303 oum.set_field("is_valid","true");
301 try { 304 try {
302 string h = inm.get_field("invalidate_handle"); 305 string h = inm.get_field("invalidate_handle");
303 try { 306 try {
304 assoc_t ih = retrieve_assoc(h); 307 assoc_t ih = retrieve_assoc(h);
305 }catch(invalid_handle& ih) { 308 }catch(invalid_handle& ih) {
306 oum.set_field("invalidate_handle",h); 309 oum.set_field("invalidate_handle",h);
307 }catch(failed_lookup& ih) { 310 }catch(failed_lookup& ih) {
308 oum.set_field("invalidate_handle",h); 311 oum.set_field("invalidate_handle",h);
309 } 312 }
310 }catch(failed_lookup&) { } 313 }catch(failed_lookup&) { }
311 if(o2) { 314 if(o2) {
312 assert(!nonce.empty()); 315 assert(!nonce.empty());
313 invalidate_nonce(nonce); 316 invalidate_nonce(nonce);
314 } 317 }
315 return oum; 318 return oum;
316 }catch(failed_check_authentication& ) { 319 }catch(failed_check_authentication& ) {
317 oum.set_field("is_valid","false"); 320 oum.set_field("is_valid","false");
318 return oum; 321 return oum;
319 } 322 }
320 323
321 void basic_op::verify_return_to() { 324 void basic_op::verify_return_to() {
322 if(realm.find('#')!=string::npos) 325 if(realm.find('#')!=string::npos)
323 throw opkele::bad_realm(OPKELE_CP_ "authentication realm contains URI fragment"); 326 throw opkele::bad_realm(OPKELE_CP_ "authentication realm contains URI fragment");
324 if(!util::uri_matches_realm(return_to,realm)) 327 if(!util::uri_matches_realm(return_to,realm))
325 throw bad_return_to(OPKELE_CP_ "return_to URL doesn't match realm"); 328 throw bad_return_to(OPKELE_CP_ "return_to URL doesn't match realm");
326 } 329 }
327 330
328} 331}
diff --git a/lib/basic_rp.cc b/lib/basic_rp.cc
index a884583..bd45d99 100644
--- a/lib/basic_rp.cc
+++ b/lib/basic_rp.cc
@@ -4,294 +4,294 @@
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*), 16 size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*),
17 size_t exp_s_len) try { 17 size_t exp_s_len) try {
18 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)
19 throw bad_input(OPKELE_CP_ "Unexpected associate response"); 19 throw bad_input(OPKELE_CP_ "Unexpected associate response");
20 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"));
21 vector<unsigned char> ck(DH_size(dh)+1); 21 vector<unsigned char> ck(DH_size(dh)+1);
22 unsigned char *ckptr = &(ck.front())+1; 22 unsigned char *ckptr = &(ck.front())+1;
23 int cklen = DH_compute_key(ckptr,s_pub,dh); 23 int cklen = DH_compute_key(ckptr,s_pub,dh);
24 if(cklen<0) 24 if(cklen<0)
25 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); 25 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()");
26 if(cklen && (*ckptr)&0x80) { 26 if(cklen && (*ckptr)&0x80) {
27 (*(--ckptr))=0; ++cklen; } 27 (*(--ckptr))=0; ++cklen; }
28 unsigned char key_digest[d_len]; 28 unsigned char key_digest[d_len];
29 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) 30 if(secret.size()!=exp_s_len)
31 throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type"); 31 throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type");
32 }catch(opkele::failed_lookup& ofl) { 32 }catch(opkele::failed_lookup& ofl) {
33 throw bad_input(OPKELE_CP_ "Incoherent response from OP"); 33 throw bad_input(OPKELE_CP_ "Incoherent response from OP");
34 } OPKELE_RETHROW 34 } OPKELE_RETHROW
35 35
36 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) {
37 util::curl_pick_t curl = util::curl_pick_t::easy_init(); 37 util::curl_pick_t curl = util::curl_pick_t::easy_init();
38 if(!curl) 38 if(!curl)
39 throw exception_curl(OPKELE_CP_ "failed to initialize curl"); 39 throw exception_curl(OPKELE_CP_ "failed to initialize curl");
40 string request = inm.query_string(); 40 string request = inm.query_string();
41 CURLcode r; 41 CURLcode r;
42 (r=curl.misc_sets()) 42 (r=curl.misc_sets())
43 || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str())) 43 || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str()))
44 || (r=curl.easy_setopt(CURLOPT_POST,1)) 44 || (r=curl.easy_setopt(CURLOPT_POST,1))
45 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) 45 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data()))
46 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) 46 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length()))
47 || (r=curl.set_write()); 47 || (r=curl.set_write());
48 if(r) 48 if(r)
49 throw exception_curl(OPKELE_CP_ "failed to set curly options",r); 49 throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
50 if( (r=curl.easy_perform()) ) 50 if( (r=curl.easy_perform()) )
51 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); 51 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
52 oum.from_keyvalues(curl.response); 52 oum.from_keyvalues(curl.response);
53 } 53 }
54 54
55 55
56 assoc_t basic_RP::associate(const string& OP) { 56 assoc_t basic_RP::associate(const string& OP) {
57 util::dh_t dh = DH_new(); 57 util::dh_t dh = DH_new();
58 if(!dh) 58 if(!dh)
59 throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); 59 throw exception_openssl(OPKELE_CP_ "failed to DH_new()");
60 dh->p = util::dec_to_bignum(data::_default_p); 60 dh->p = util::dec_to_bignum(data::_default_p);
61 dh->g = util::dec_to_bignum(data::_default_g); 61 dh->g = util::dec_to_bignum(data::_default_g);
62 if(!DH_generate_key(dh)) 62 if(!DH_generate_key(dh))
63 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); 63 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()");
64 openid_message_t req; 64 openid_message_t req;
65 req.set_field("ns",OIURI_OPENID20); 65 req.set_field("ns",OIURI_OPENID20);
66 req.set_field("mode","associate"); 66 req.set_field("mode","associate");
67 req.set_field("dh_modulus",util::bignum_to_base64(dh->p)); 67 req.set_field("dh_modulus",util::bignum_to_base64(dh->p));
68 req.set_field("dh_gen",util::bignum_to_base64(dh->g)); 68 req.set_field("dh_gen",util::bignum_to_base64(dh->g));
69 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));
70 openid_message_t res; 70 openid_message_t res;
71 req.set_field("assoc_type","HMAC-SHA256"); 71 req.set_field("assoc_type","HMAC-SHA256");
72 req.set_field("session_type","DH-SHA256"); 72 req.set_field("session_type","DH-SHA256");
73 secret_t secret; 73 secret_t secret;
74 int expires_in; 74 int expires_in;
75 try { 75 try {
76 direct_request(res,req,OP); 76 direct_request(res,req,OP);
77 dh_get_secret( secret, res, 77 dh_get_secret( secret, res,
78 "HMAC-SHA256", "DH-SHA256", 78 "HMAC-SHA256", "DH-SHA256",
79 dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH ); 79 dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH );
80 expires_in = util::string_to_long(res.get_field("expires_in")); 80 expires_in = util::string_to_long(res.get_field("expires_in"));
81 }catch(exception& e) { 81 }catch(exception& e) {
82 try { 82 try {
83 req.set_field("assoc_type","HMAC-SHA1"); 83 req.set_field("assoc_type","HMAC-SHA1");
84 req.set_field("session_type","DH-SHA1"); 84 req.set_field("session_type","DH-SHA1");
85 direct_request(res,req,OP); 85 direct_request(res,req,OP);
86 dh_get_secret( secret, res, 86 dh_get_secret( secret, res,
87 "HMAC-SHA1", "DH-SHA1", 87 "HMAC-SHA1", "DH-SHA1",
88 dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH ); 88 dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH );
89 expires_in = util::string_to_long(res.get_field("expires_in")); 89 expires_in = util::string_to_long(res.get_field("expires_in"));
90 }catch(bad_input& e) { 90 }catch(bad_input& e) {
91 throw dumb_RP(OPKELE_CP_ "OP failed to supply an association"); 91 throw dumb_RP(OPKELE_CP_ "OP failed to supply an association");
92 } 92 }
93 } 93 }
94 return store_assoc( 94 return store_assoc(
95 OP, res.get_field("assoc_handle"), 95 OP, res.get_field("assoc_handle"),
96 res.get_field("assoc_type"), secret, 96 res.get_field("assoc_type"), secret,
97 expires_in ); 97 expires_in );
98 } 98 }
99 99
100 basic_openid_message& basic_RP::checkid_( 100 basic_openid_message& basic_RP::checkid_(
101 basic_openid_message& rv, 101 basic_openid_message& rv,
102 mode_t mode, 102 mode_t mode,
103 const string& return_to,const string& realm, 103 const string& return_to,const string& realm,
104 extension_t *ext) { 104 extension_t *ext) {
105 rv.reset_fields(); 105 rv.reset_fields();
106 rv.set_field("ns",OIURI_OPENID20); 106 rv.set_field("ns",OIURI_OPENID20);
107 if(mode==mode_checkid_immediate) 107 if(mode==mode_checkid_immediate)
108 rv.set_field("mode","checkid_immediate"); 108 rv.set_field("mode","checkid_immediate");
109 else if(mode==mode_checkid_setup) 109 else if(mode==mode_checkid_setup)
110 rv.set_field("mode","checkid_setup"); 110 rv.set_field("mode","checkid_setup");
111 else 111 else
112 throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); 112 throw bad_input(OPKELE_CP_ "unknown checkid_* mode");
113 if(realm.empty() && return_to.empty()) 113 if(realm.empty() && return_to.empty())
114 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");
115 if(!realm.empty()) { 115 if(!realm.empty()) {
116 rv.set_field("realm",realm); 116 rv.set_field("realm",realm);
117 rv.set_field("trust_root",realm); 117 rv.set_field("trust_root",realm);
118 } 118 }
119 if(!return_to.empty()) 119 if(!return_to.empty())
120 rv.set_field("return_to",return_to); 120 rv.set_field("return_to",return_to);
121 const openid_endpoint_t& ep = get_endpoint(); 121 const openid_endpoint_t& ep = get_endpoint();
122 rv.set_field("claimed_id",ep.claimed_id); 122 rv.set_field("claimed_id",ep.claimed_id);
123 rv.set_field("identity",ep.local_id); 123 rv.set_field("identity",ep.local_id);
124 try { 124 try {
125 rv.set_field("assoc_handle",find_assoc(ep.uri)->handle()); 125 rv.set_field("assoc_handle",find_assoc(ep.uri)->handle());
126 }catch(dumb_RP& drp) { 126 }catch(dumb_RP& drp) {
127 }catch(failed_lookup& fl) { 127 }catch(failed_lookup& fl) {
128 try { 128 try {
129 rv.set_field("assoc_handle",associate(ep.uri)->handle()); 129 rv.set_field("assoc_handle",associate(ep.uri)->handle());
130 }catch(dumb_RP& drp) { } 130 }catch(dumb_RP& drp) { }
131 } OPKELE_RETHROW 131 } OPKELE_RETHROW
132 if(ext) ext->checkid_hook(rv); 132 if(ext) ext->rp_checkid_hook(rv);
133 return rv; 133 return rv;
134 } 134 }
135 135
136 class signed_part_message_proxy : public basic_openid_message { 136 class signed_part_message_proxy : public basic_openid_message {
137 public: 137 public:
138 const basic_openid_message& x; 138 const basic_openid_message& x;
139 set<string> signeds; 139 set<string> signeds;
140 140
141 signed_part_message_proxy(const basic_openid_message& xx) : x(xx) { 141 signed_part_message_proxy(const basic_openid_message& xx) : x(xx) {
142 const string& slist = x.get_field("signed"); 142 const string& slist = x.get_field("signed");
143 string::size_type p = 0; 143 string::size_type p = 0;
144 while(true) { 144 while(true) {
145 string::size_type co = slist.find(',',p); 145 string::size_type co = slist.find(',',p);
146 string f = (co==string::npos) 146 string f = (co==string::npos)
147 ?slist.substr(p):slist.substr(p,co-p); 147 ?slist.substr(p):slist.substr(p,co-p);
148 signeds.insert(f); 148 signeds.insert(f);
149 if(co==string::npos) break; 149 if(co==string::npos) break;
150 p = co+1; 150 p = co+1;
151 } 151 }
152 } 152 }
153 153
154 bool has_field(const string& n) const { 154 bool has_field(const string& n) const {
155 return signeds.find(n)!=signeds.end() && x.has_field(n); } 155 return signeds.find(n)!=signeds.end() && x.has_field(n); }
156 const string& get_field(const string& n) const { 156 const string& get_field(const string& n) const {
157 if(signeds.find(n)==signeds.end()) 157 if(signeds.find(n)==signeds.end())
158 throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed"); 158 throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed");
159 return x.get_field(n); } 159 return x.get_field(n); }
160 160
161 fields_iterator fields_begin() const { 161 fields_iterator fields_begin() const {
162 return signeds.begin(); } 162 return signeds.begin(); }
163 fields_iterator fields_end() const { 163 fields_iterator fields_end() const {
164 return signeds.end(); } 164 return signeds.end(); }
165 }; 165 };
166 166
167 static void parse_query(const string& u,string::size_type q, 167 static void parse_query(const string& u,string::size_type q,
168 map<string,string>& p) { 168 map<string,string>& p) {
169 if(q==string::npos) 169 if(q==string::npos)
170 return; 170 return;
171 assert(u[q]=='?'); 171 assert(u[q]=='?');
172 ++q; 172 ++q;
173 string::size_type l = u.size(); 173 string::size_type l = u.size();
174 while(q<l) { 174 while(q<l) {
175 string::size_type eq = u.find('=',q); 175 string::size_type eq = u.find('=',q);
176 string::size_type am = u.find('&',q); 176 string::size_type am = u.find('&',q);
177 if(am==string::npos) { 177 if(am==string::npos) {
178 if(eq==string::npos) { 178 if(eq==string::npos) {
179 p[""] = u.substr(q); 179 p[""] = u.substr(q);
180 }else{ 180 }else{
181 p[u.substr(q,eq-q)] = u.substr(eq+1); 181 p[u.substr(q,eq-q)] = u.substr(eq+1);
182 } 182 }
183 break; 183 break;
184 }else{ 184 }else{
185 if(eq==string::npos || eq>am) { 185 if(eq==string::npos || eq>am) {
186 p[""] = u.substr(q,eq-q); 186 p[""] = u.substr(q,eq-q);
187 }else{ 187 }else{
188 p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1); 188 p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1);
189 } 189 }
190 q = ++am; 190 q = ++am;
191 } 191 }
192 } 192 }
193 } 193 }
194 194
195 void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) { 195 void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) {
196 bool o2 = om.has_field("ns") 196 bool o2 = om.has_field("ns")
197 && om.get_field("ns")==OIURI_OPENID20; 197 && om.get_field("ns")==OIURI_OPENID20;
198 if( (!o2) && om.has_field("user_setup_url")) 198 if( (!o2) && om.has_field("user_setup_url"))
199 throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided", 199 throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",
200 om.get_field("user_setup_url")); 200 om.get_field("user_setup_url"));
201 string m = om.get_field("mode"); 201 string m = om.get_field("mode");
202 if(o2 && m=="setup_needed") 202 if(o2 && m=="setup_needed")
203 throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided"); 203 throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided");
204 if(m=="cancel") 204 if(m=="cancel")
205 throw id_res_cancel(OPKELE_CP_ "authentication cancelled"); 205 throw id_res_cancel(OPKELE_CP_ "authentication cancelled");
206 bool go_dumb=false; 206 bool go_dumb=false;
207 try { 207 try {
208 string OP = o2 208 string OP = o2
209 ?om.get_field("op_endpoint") 209 ?om.get_field("op_endpoint")
210 :get_endpoint().uri; 210 :get_endpoint().uri;
211 assoc_t assoc = retrieve_assoc( 211 assoc_t assoc = retrieve_assoc(
212 OP,om.get_field("assoc_handle")); 212 OP,om.get_field("assoc_handle"));
213 if(om.get_field("sig")!=util::base64_signature(assoc,om)) 213 if(om.get_field("sig")!=util::base64_signature(assoc,om))
214 throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); 214 throw id_res_mismatch(OPKELE_CP_ "signature mismatch");
215 }catch(dumb_RP& drp) { 215 }catch(dumb_RP& drp) {
216 go_dumb=true; 216 go_dumb=true;
217 }catch(failed_lookup& e) { 217 }catch(failed_lookup& e) {
218 go_dumb=true; 218 go_dumb=true;
219 } OPKELE_RETHROW 219 } OPKELE_RETHROW
220 if(go_dumb) { 220 if(go_dumb) {
221 try { 221 try {
222 string OP = o2 222 string OP = o2
223 ?om.get_field("op_endpoint") 223 ?om.get_field("op_endpoint")
224 :get_endpoint().uri; 224 :get_endpoint().uri;
225 check_authentication(OP,om); 225 check_authentication(OP,om);
226 }catch(failed_check_authentication& fca) { 226 }catch(failed_check_authentication& fca) {
227 throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); 227 throw id_res_failed(OPKELE_CP_ "failed to check_authentication()");
228 } OPKELE_RETHROW 228 } OPKELE_RETHROW
229 } 229 }
230 signed_part_message_proxy signeds(om); 230 signed_part_message_proxy signeds(om);
231 if(o2) { 231 if(o2) {
232 check_nonce(om.get_field("op_endpoint"), 232 check_nonce(om.get_field("op_endpoint"),
233 om.get_field("response_nonce")); 233 om.get_field("response_nonce"));
234 static const char *mustsign[] = { 234 static const char *mustsign[] = {
235 "op_endpoint", "return_to", "response_nonce", "assoc_handle", 235 "op_endpoint", "return_to", "response_nonce", "assoc_handle",
236 "claimed_id", "identity" }; 236 "claimed_id", "identity" };
237 for(int ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) { 237 for(int ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) {
238 if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms])) 238 if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms]))
239 throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs"); 239 throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs");
240 } 240 }
241 if( ( 241 if( (
242 (om.has_field("claimed_id")?1:0) 242 (om.has_field("claimed_id")?1:0)
243 ^ 243 ^
244 (om.has_field("identity")?1:0) 244 (om.has_field("identity")?1:0)
245 )&1 ) 245 )&1 )
246 throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent"); 246 throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent");
247 247
248 string turl = util::rfc_3986_normalize_uri(get_this_url()); 248 string turl = util::rfc_3986_normalize_uri(get_this_url());
249 util::strip_uri_fragment_part(turl); 249 util::strip_uri_fragment_part(turl);
250 string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to")); 250 string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to"));
251 util::strip_uri_fragment_part(rurl); 251 util::strip_uri_fragment_part(rurl);
252 string::size_type 252 string::size_type
253 tq = turl.find('?'), rq = rurl.find('?'); 253 tq = turl.find('?'), rq = rurl.find('?');
254 if( 254 if(
255 ((tq==string::npos)?turl:turl.substr(0,tq)) 255 ((tq==string::npos)?turl:turl.substr(0,tq))
256 != 256 !=
257 ((rq==string::npos)?rurl:rurl.substr(0,rq)) 257 ((rq==string::npos)?rurl:rurl.substr(0,rq))
258 ) 258 )
259 throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url"); 259 throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url");
260 map<string,string> tp; parse_query(turl,tq,tp); 260 map<string,string> tp; parse_query(turl,tq,tp);
261 map<string,string> rp; parse_query(rurl,rq,rp); 261 map<string,string> rp; parse_query(rurl,rq,rp);
262 for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) { 262 for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) {
263 map<string,string>::const_iterator tpi = tp.find(rpi->first); 263 map<string,string>::const_iterator tpi = tp.find(rpi->first);
264 if(tpi==tp.end()) 264 if(tpi==tp.end())
265 throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request"); 265 throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request");
266 if(tpi->second!=rpi->second) 266 if(tpi->second!=rpi->second)
267 throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request"); 267 throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request");
268 } 268 }
269 269
270 if(om.has_field("claimed_id")) { 270 if(om.has_field("claimed_id")) {
271 verify_OP( 271 verify_OP(
272 om.get_field("op_endpoint"), 272 om.get_field("op_endpoint"),
273 om.get_field("claimed_id"), 273 om.get_field("claimed_id"),
274 om.get_field("identity") ); 274 om.get_field("identity") );
275 } 275 }
276 276
277 } 277 }
278 if(ext) ext->id_res_hook(om,signeds); 278 if(ext) ext->rp_id_res_hook(om,signeds);
279 } 279 }
280 280
281 void basic_RP::check_authentication(const string& OP, 281 void basic_RP::check_authentication(const string& OP,
282 const basic_openid_message& om){ 282 const basic_openid_message& om){
283 openid_message_t res; 283 openid_message_t res;
284 static const string checkauthmode = "check_authentication"; 284 static const string checkauthmode = "check_authentication";
285 direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP); 285 direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP);
286 if(res.has_field("is_valid")) { 286 if(res.has_field("is_valid")) {
287 if(res.get_field("is_valid")=="true") { 287 if(res.get_field("is_valid")=="true") {
288 if(res.has_field("invalidate_handle")) 288 if(res.has_field("invalidate_handle"))
289 invalidate_assoc(OP,res.get_field("invalidate_handle")); 289 invalidate_assoc(OP,res.get_field("invalidate_handle"));
290 return; 290 return;
291 } 291 }
292 } 292 }
293 throw failed_check_authentication( 293 throw failed_check_authentication(
294 OPKELE_CP_ "failed to verify response"); 294 OPKELE_CP_ "failed to verify response");
295 } 295 }
296 296
297} 297}
diff --git a/lib/extension.cc b/lib/extension.cc
index 6451249..f7aaea5 100644
--- a/lib/extension.cc
+++ b/lib/extension.cc
@@ -1,15 +1,26 @@
1#include <opkele/exception.h> 1#include <opkele/exception.h>
2#include <opkele/extension.h> 2#include <opkele/extension.h>
3 3
4namespace opkele { 4namespace opkele {
5 5
6 void extension_t::rp_checkid_hook(basic_openid_message&) {
7 throw not_implemented(OPKELE_CP_ "RP checkid_* hook not implemented"); }
8 void extension_t::rp_id_res_hook(const basic_openid_message&,
9 const basic_openid_message&) {
10 throw not_implemented(OPKELE_CP_ "RP id_res hook not implemented"); }
11
12 void extension_t::op_checkid_hook(const basic_openid_message&) {
13 throw not_implemented(OPKELE_CP_ "OP checkid_* hook not implemented"); }
14 void extension_t::op_id_res_hook(basic_openid_message& om) {
15 throw not_implemented(OPKELE_CP_ "OP id_res hook not implemented"); }
16
17
6 void extension_t::checkid_hook(basic_openid_message&) { 18 void extension_t::checkid_hook(basic_openid_message&) {
7 throw not_implemented(OPKELE_CP_ "Consumer checkid_hook not implemented"); 19 throw not_implemented(OPKELE_CP_ "deprecated consumer checkid_* hook not implemented"); }
8 } 20 void extension_t::id_res_hook(const basic_openid_message&,
9 void extension_t::id_res_hook(const basic_openid_message&,const basic_openid_message&) { 21 const basic_openid_message&) {
10 throw not_implemented(OPKELE_CP_ "Consumer id_res_hook not implemented"); 22 throw not_implemented(OPKELE_CP_ "deprecated consumer id_res hook not implemented"); }
11 } 23
12 void extension_t::checkid_hook(const basic_openid_message&,basic_openid_message&) { 24 void extension_t::checkid_hook(const basic_openid_message&,basic_openid_message&) {
13 throw not_implemented(OPKELE_CP_ "Server checkid_hook not implemented"); 25 throw not_implemented(OPKELE_CP_ "deprecated server checkid hook not implemented"); }
14 }
15} 26}
diff --git a/lib/extension_chain.cc b/lib/extension_chain.cc
index 5c2afd9..5483740 100644
--- a/lib/extension_chain.cc
+++ b/lib/extension_chain.cc
@@ -1,16 +1,27 @@
1#include <cstdarg> 1#include <cstdarg>
2#include <opkele/extension_chain.h> 2#include <opkele/extension_chain.h>
3 3
4namespace opkele { 4namespace opkele {
5 5
6 void extension_chain_t::rp_checkid_hook(basic_openid_message& om) {
7 for(iterator i=begin();i!=end();++i) (*i)->rp_checkid_hook(om); }
8 void extension_chain_t::rp_id_res_hook(const basic_openid_message& om,
9 const basic_openid_message& sp) {
10 for(iterator i=begin();i!=end();++i) (*i)->rp_id_res_hook(om,sp); }
11
12 void extension_chain_t::op_checkid_hook(const basic_openid_message& inm) {
13 for(iterator i=begin();i!=end();++i) (*i)->op_checkid_hook(inm); }
14 void extension_chain_t::op_id_res_hook(basic_openid_message& oum) {
15 for(iterator i=begin();i!=end();++i) (*i)->op_id_res_hook(oum); }
16
17
6 void extension_chain_t::checkid_hook(basic_openid_message& om){ 18 void extension_chain_t::checkid_hook(basic_openid_message& om){
7 for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(om); 19 for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(om); }
8 } 20 void extension_chain_t::id_res_hook(const basic_openid_message& om,
9 void extension_chain_t::id_res_hook(const basic_openid_message& om,const basic_openid_message& sp) { 21 const basic_openid_message& sp) {
10 for(iterator i=begin();i!=end();++i) (*i)->id_res_hook(om,sp); 22 for(iterator i=begin();i!=end();++i) (*i)->id_res_hook(om,sp); }
11 } 23 void extension_chain_t::checkid_hook(const basic_openid_message& inm,
12 void extension_chain_t::checkid_hook(const basic_openid_message& inm,basic_openid_message& oum) { 24 basic_openid_message& oum) {
13 for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(inm,oum); 25 for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(inm,oum); }
14 }
15 26
16} 27}
diff --git a/lib/sreg.cc b/lib/sreg.cc
index 7e2d588..b40cd45 100644
--- a/lib/sreg.cc
+++ b/lib/sreg.cc
@@ -1,140 +1,160 @@
1#include <opkele/exception.h> 1#include <opkele/exception.h>
2#include <opkele/sreg.h> 2#include <opkele/sreg.h>
3#include <opkele/uris.h> 3#include <opkele/uris.h>
4#include <algorithm> 4#include <algorithm>
5 5
6namespace opkele { 6namespace opkele {
7 using std::find; 7 using std::find;
8 8
9 static const struct _sreg_field { 9 static const struct _sreg_field {
10 const char *fieldname; 10 const char *fieldname;
11 sreg_t::fieldbit_t fieldbit; 11 sreg_t::fieldbit_t fieldbit;
12 }fields[] = { 12 }fields[] = {
13 { "nickname", sreg_t::field_nickname }, 13 { "nickname", sreg_t::field_nickname },
14 { "email", sreg_t::field_email }, 14 { "email", sreg_t::field_email },
15 { "fullname", sreg_t::field_fullname }, 15 { "fullname", sreg_t::field_fullname },
16 { "dob", sreg_t::field_dob }, 16 { "dob", sreg_t::field_dob },
17 { "gender", sreg_t::field_gender }, 17 { "gender", sreg_t::field_gender },
18 { "postcode", sreg_t::field_postcode }, 18 { "postcode", sreg_t::field_postcode },
19 { "country", sreg_t::field_country }, 19 { "country", sreg_t::field_country },
20 { "language", sreg_t::field_language }, 20 { "language", sreg_t::field_language },
21 { "timezone", sreg_t::field_timezone } 21 { "timezone", sreg_t::field_timezone }
22 }; 22 };
23 # define fields_BEGINfields 23 # define fields_BEGINfields
24# define fields_END &fields[sizeof(fields)/sizeof(*fields)] 24# define fields_END &fields[sizeof(fields)/sizeof(*fields)]
25 typedef const struct _sreg_field *fields_iterator; 25 typedef const struct _sreg_field *fields_iterator;
26 26
27 bool operator==(const struct _sreg_field& fd,const string& fn) { 27 bool operator==(const struct _sreg_field& fd,const string& fn) {
28 return fd.fieldname==fn; 28 return fd.fieldname==fn;
29 } 29 }
30 30
31 void sreg_t::checkid_hook(basic_openid_message& om) { 31 void sreg_t::rp_checkid_hook(basic_openid_message& om) {
32 string fr, fo; 32 string fr, fo;
33 for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { 33 for(fields_iterator f=fields_BEGIN;f<fields_END;++f) {
34 if(f->fieldbit&fields_required) { 34 if(f->fieldbit&fields_required) {
35 if(!fr.empty()) fr+=","; 35 if(!fr.empty()) fr+=",";
36 fr += f->fieldname; 36 fr += f->fieldname;
37 } 37 }
38 if(f->fieldbit&fields_optional) { 38 if(f->fieldbit&fields_optional) {
39 if(!fo.empty()) fo+=","; 39 if(!fo.empty()) fo+=",";
40 fo += f->fieldname; 40 fo += f->fieldname;
41 } 41 }
42 } 42 }
43 string pfx = om.allocate_ns(OIURI_SREG11,"sreg"); 43 string pfx = om.allocate_ns(OIURI_SREG11,"sreg");
44 if(!fr.empty()) om.set_field(pfx+".required",fr); 44 if(!fr.empty()) om.set_field(pfx+".required",fr);
45 if(!fo.empty()) om.set_field(pfx+".optional",fo); 45 if(!fo.empty()) om.set_field(pfx+".optional",fo);
46 if(!policy_url.empty()) om.set_field(pfx+".policy_url",policy_url); 46 if(!policy_url.empty()) om.set_field(pfx+".policy_url",policy_url);
47 } 47 }
48 48
49 void sreg_t::id_res_hook(const basic_openid_message& om,const basic_openid_message& sp) { 49 void sreg_t::checkid_hook(basic_openid_message& om) {
50 rp_checkid_hook(om); }
51
52 void sreg_t::rp_id_res_hook(const basic_openid_message& om,
53 const basic_openid_message& sp) {
50 clear(); 54 clear();
51 string pfx; 55 string pfx;
52 try { 56 try {
53 pfx = om.find_ns(OIURI_SREG11,"sreg"); 57 pfx = om.find_ns(OIURI_SREG11,"sreg");
54 }catch(failed_lookup& fl) { 58 }catch(failed_lookup& fl) {
55 try { 59 try {
56 pfx = om.find_ns(OIURI_SREG10,"sreg"); 60 pfx = om.find_ns(OIURI_SREG10,"sreg");
57 }catch(failed_lookup& fl) { 61 }catch(failed_lookup& fl) {
58 return; 62 return;
59 } 63 }
60 } 64 }
61 pfx += '.'; 65 pfx += '.';
62 for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { 66 for(fields_iterator f=fields_BEGIN;f<fields_END;++f) {
63 string fn = pfx; fn+=f->fieldname; 67 string fn = pfx; fn+=f->fieldname;
64 if(!sp.has_field(fn)) continue; 68 if(!sp.has_field(fn)) continue;
65 has_fields |= f->fieldbit; 69 has_fields |= f->fieldbit;
66 response[f->fieldbit]=sp.get_field(fn); 70 response[f->fieldbit]=sp.get_field(fn);
67 } 71 }
68 } 72 }
69 73
74 void sreg_t::id_res_hook(const basic_openid_message& om,
75 const basic_openid_message& sp) {
76 rp_id_res_hook(om,sp); }
77
70 const string& sreg_t::get_field(fieldbit_t fb) const { 78 const string& sreg_t::get_field(fieldbit_t fb) const {
71 response_t::const_iterator i = response.find(fb); 79 response_t::const_iterator i = response.find(fb);
72 if(i==response.end()) 80 if(i==response.end())
73 throw failed_lookup(OPKELE_CP_ "no field data available"); 81 throw failed_lookup(OPKELE_CP_ "no field data available");
74 return i->second; 82 return i->second;
75 } 83 }
76 84
77 void sreg_t::set_field(fieldbit_t fb,const string& fv) { 85 void sreg_t::set_field(fieldbit_t fb,const string& fv) {
78 response[fb] = fv; 86 response[fb] = fv;
79 has_fields |= fb; 87 has_fields |= fb;
80 } 88 }
81 89
82 void sreg_t::reset_field(fieldbit_t fb) { 90 void sreg_t::reset_field(fieldbit_t fb) {
83 has_fields &= ~fb; 91 has_fields &= ~fb;
84 response.erase(fb); 92 response.erase(fb);
85 } 93 }
86 94
87 void sreg_t::clear() { 95 void sreg_t::clear() {
88 has_fields = 0; response.clear(); 96 has_fields = 0; response.clear();
89 } 97 }
90 98
91 static long fields_list_to_bitmask(string& fl) { 99 static long fields_list_to_bitmask(string& fl) {
92 long rv = 0; 100 long rv = 0;
93 while(!fl.empty()) { 101 while(!fl.empty()) {
94 string::size_type co = fl.find(','); 102 string::size_type co = fl.find(',');
95 string fn; 103 string fn;
96 if(co==string::npos) { 104 if(co==string::npos) {
97 fn = fl; fl.erase(); 105 fn = fl; fl.erase();
98 }else{ 106 }else{
99 fn = fl.substr(0,co); fl.erase(0,co+1); 107 fn = fl.substr(0,co); fl.erase(0,co+1);
100 } 108 }
101 fields_iterator f = find(fields_BEGIN,fields_END,fn); 109 fields_iterator f = find(fields_BEGIN,fields_END,fn);
102 if(f!=fields_END) 110 if(f!=fields_END)
103 rv |= f->fieldbit; 111 rv |= f->fieldbit;
104 } 112 }
105 return rv; 113 return rv;
106 } 114 }
107 115
108 void sreg_t::checkid_hook(const basic_openid_message& inm,basic_openid_message& oum) { 116 void sreg_t::op_checkid_hook(const basic_openid_message& inm) {
109 string ins = inm.find_ns(OIURI_SREG11,"sreg"); 117 string ins = inm.find_ns(OIURI_SREG11,"sreg");
110 fields_optional = 0; fields_required = 0; policy_url.erase(); 118 fields_optional = 0; fields_required = 0; policy_url.erase();
111 fields_response = 0; 119 fields_response = 0;
112 try { 120 try {
113 string fl = inm.get_field(ins+".required"); 121 string fl = inm.get_field(ins+".required");
114 fields_required = fields_list_to_bitmask(fl); 122 fields_required = fields_list_to_bitmask(fl);
115 }catch(failed_lookup&) { } 123 }catch(failed_lookup&) { }
116 try { 124 try {
117 string fl = inm.get_field(ins+".optional"); 125 string fl = inm.get_field(ins+".optional");
118 fields_optional = fields_list_to_bitmask(fl); 126 fields_optional = fields_list_to_bitmask(fl);
119 }catch(failed_lookup&) { } 127 }catch(failed_lookup&) { }
120 try { 128 try {
121 policy_url = inm.get_field(ins+".policy_url"); 129 policy_url = inm.get_field(ins+".policy_url");
122 }catch(failed_lookup&) { } 130 }catch(failed_lookup&) { }
123 setup_response(inm,oum); 131 }
132
133 void sreg_t::op_id_res_hook(basic_openid_message& oum) {
124 string ons = oum.allocate_ns(OIURI_SREG11,"sreg"); 134 string ons = oum.allocate_ns(OIURI_SREG11,"sreg");
125 fields_response &= has_fields; 135 fields_response &= has_fields;
126 string signeds = "ns."+ons; 136 string signeds = "ns."+ons;
127 for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { 137 for(fields_iterator f=fields_BEGIN;f<fields_END;++f) {
128 if(!(f->fieldbit&fields_response)) continue; 138 if(!(f->fieldbit&fields_response)) continue;
129 signeds +=','; 139 signeds +=',';
130 string pn = ons; pn += '.'; pn += f->fieldname; 140 string pn = ons; pn += '.'; pn += f->fieldname;
131 signeds += pn; 141 signeds += pn;
132 oum.set_field(pn,get_field(f->fieldbit)); 142 oum.set_field(pn,get_field(f->fieldbit));
133 } 143 }
134 oum.add_to_signed(signeds); 144 oum.add_to_signed(signeds);
135 } 145 }
136 146
147 void sreg_t::checkid_hook(const basic_openid_message& inm,
148 basic_openid_message& oum) {
149 op_checkid_hook(inm);
150 setup_response(inm,oum);
151 op_id_res_hook(oum);
152 }
153
137 void sreg_t::setup_response(const basic_openid_message& /* inm */,basic_openid_message& /* oum */) { 154 void sreg_t::setup_response(const basic_openid_message& /* inm */,basic_openid_message& /* oum */) {
155 setup_response();
156 }
157 void sreg_t::setup_response() {
138 fields_response = (fields_required|fields_optional)&has_fields; 158 fields_response = (fields_required|fields_optional)&has_fields;
139 } 159 }
140} 160}