-rw-r--r-- | lib/Makefile.am | 7 | ||||
-rw-r--r-- | lib/basic_rp.cc | 311 | ||||
-rw-r--r-- | lib/consumer.cc | 4 | ||||
-rw-r--r-- | lib/discovery.cc | 161 | ||||
-rw-r--r-- | lib/extension.cc | 6 | ||||
-rw-r--r-- | lib/extension_chain.cc | 12 | ||||
-rw-r--r-- | lib/openid_message.cc | 228 | ||||
-rw-r--r-- | lib/params.cc | 101 | ||||
-rw-r--r-- | lib/prequeue_rp.cc | 81 | ||||
-rw-r--r-- | lib/server.cc | 2 | ||||
-rw-r--r-- | lib/sreg.cc | 54 | ||||
-rw-r--r-- | lib/util.cc | 71 |
12 files changed, 871 insertions, 167 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 989de28..c58ec3f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am | |||
@@ -5,3 +5,3 @@ DEFAULT_INCLUDES = -I${top_builddir} | |||
5 | INCLUDES = \ | 5 | INCLUDES = \ |
6 | -I${top_srcdir}/include/ \ | 6 | -I${top_builddir}/include/ -I${top_srcdir}/include/ \ |
7 | ${KONFORKA_CFLAGS} \ | 7 | ${KONFORKA_CFLAGS} \ |
@@ -28,3 +28,6 @@ libopkele_la_SOURCES = \ | |||
28 | curl.cc expat.cc \ | 28 | curl.cc expat.cc \ |
29 | discovery.cc | 29 | discovery.cc \ |
30 | basic_rp.cc \ | ||
31 | prequeue_rp.cc \ | ||
32 | openid_message.cc | ||
30 | libopkele_la_LDFLAGS = \ | 33 | libopkele_la_LDFLAGS = \ |
diff --git a/lib/basic_rp.cc b/lib/basic_rp.cc new file mode 100644 index 0000000..763a391 --- a/dev/null +++ b/lib/basic_rp.cc | |||
@@ -0,0 +1,311 @@ | |||
1 | #include <openssl/sha.h> | ||
2 | #include <openssl/hmac.h> | ||
3 | #include <opkele/basic_rp.h> | ||
4 | #include <opkele/exception.h> | ||
5 | #include <opkele/uris.h> | ||
6 | #include <opkele/data.h> | ||
7 | #include <opkele/util.h> | ||
8 | #include <opkele/curl.h> | ||
9 | |||
10 | namespace opkele { | ||
11 | |||
12 | static void dh_get_secret( | ||
13 | secret_t& secret, const basic_openid_message& om, | ||
14 | const char *exp_assoc, const char *exp_sess, | ||
15 | util::dh_t& dh, | ||
16 | size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*) ) try { | ||
17 | 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 | 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 | unsigned char *ckptr = &(ck.front())+1; | ||
22 | int cklen = DH_compute_key(ckptr,s_pub,dh); | ||
23 | if(cklen<0) | ||
24 | throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); | ||
25 | if(cklen && (*ckptr)&0x80) { | ||
26 | (*(--ckptr))=0; ++cklen; } | ||
27 | unsigned char key_digest[d_len]; | ||
28 | secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key")); | ||
29 | }catch(opkele::failed_lookup& ofl) { | ||
30 | throw bad_input(OPKELE_CP_ "Incoherent response from OP"); | ||
31 | } OPKELE_RETHROW | ||
32 | |||
33 | 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(); | ||
35 | if(!curl) | ||
36 | throw exception_curl(OPKELE_CP_ "failed to initialize curl"); | ||
37 | string request = inm.query_string(); | ||
38 | CURLcode r; | ||
39 | (r=curl.misc_sets()) | ||
40 | || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str())) | ||
41 | || (r=curl.easy_setopt(CURLOPT_POST,1)) | ||
42 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) | ||
43 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) | ||
44 | || (r=curl.set_write()); | ||
45 | if(r) | ||
46 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); | ||
47 | if( (r=curl.easy_perform()) ) | ||
48 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); | ||
49 | oum.from_keyvalues(curl.response); | ||
50 | } | ||
51 | |||
52 | |||
53 | assoc_t basic_RP::associate(const string& OP) { | ||
54 | util::dh_t dh = DH_new(); | ||
55 | if(!dh) | ||
56 | throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); | ||
57 | dh->p = util::dec_to_bignum(data::_default_p); | ||
58 | dh->g = util::dec_to_bignum(data::_default_g); | ||
59 | if(!DH_generate_key(dh)) | ||
60 | throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); | ||
61 | openid_message_t req; | ||
62 | req.set_field("ns",OIURI_OPENID20); | ||
63 | req.set_field("mode","associate"); | ||
64 | req.set_field("dh_modulus",util::bignum_to_base64(dh->p)); | ||
65 | 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)); | ||
67 | openid_message_t res; | ||
68 | req.set_field("assoc_type","HMAC-SHA256"); | ||
69 | req.set_field("session_type","DH-SHA256"); | ||
70 | secret_t secret; | ||
71 | int expires_in; | ||
72 | try { | ||
73 | direct_request(res,req,OP); | ||
74 | dh_get_secret( secret, res, | ||
75 | "HMAC-SHA256", "DH-SHA256", | ||
76 | dh, SHA256_DIGEST_LENGTH, SHA256 ); | ||
77 | expires_in = util::string_to_long(res.get_field("expires_in")); | ||
78 | }catch(exception& e) { | ||
79 | try { | ||
80 | req.set_field("assoc_type","HMAC-SHA1"); | ||
81 | req.set_field("session_type","DH-SHA1"); | ||
82 | direct_request(res,req,OP); | ||
83 | dh_get_secret( secret, res, | ||
84 | "HMAC-SHA1", "DH-SHA1", | ||
85 | dh, SHA_DIGEST_LENGTH, SHA1 ); | ||
86 | expires_in = util::string_to_long(res.get_field("expires_in")); | ||
87 | }catch(bad_input& e) { | ||
88 | throw dumb_RP(OPKELE_CP_ "OP failed to supply an association"); | ||
89 | } | ||
90 | } | ||
91 | return store_assoc( | ||
92 | OP, res.get_field("assoc_handle"), | ||
93 | res.get_field("assoc_type"), secret, | ||
94 | expires_in ); | ||
95 | } | ||
96 | |||
97 | basic_openid_message& basic_RP::checkid_( | ||
98 | basic_openid_message& rv, | ||
99 | mode_t mode, | ||
100 | const string& return_to,const string& realm, | ||
101 | extension_t *ext) { | ||
102 | rv.reset_fields(); | ||
103 | rv.set_field("ns",OIURI_OPENID20); | ||
104 | if(mode==mode_checkid_immediate) | ||
105 | rv.set_field("mode","checkid_immediate"); | ||
106 | else if(mode==mode_checkid_setup) | ||
107 | rv.set_field("mode","checkid_setup"); | ||
108 | else | ||
109 | throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); | ||
110 | if(realm.empty() && return_to.empty()) | ||
111 | throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty"); | ||
112 | if(!realm.empty()) { | ||
113 | rv.set_field("realm",realm); | ||
114 | rv.set_field("trust_root",realm); | ||
115 | } | ||
116 | if(!return_to.empty()) | ||
117 | rv.set_field("return_to",return_to); | ||
118 | const openid_endpoint_t& ep = get_endpoint(); | ||
119 | rv.set_field("claimed_id",ep.claimed_id); | ||
120 | rv.set_field("identity",ep.local_id); | ||
121 | try { | ||
122 | rv.set_field("assoc_handle",find_assoc(ep.uri)->handle()); | ||
123 | }catch(dumb_RP& drp) { | ||
124 | }catch(failed_lookup& fl) { | ||
125 | try { | ||
126 | rv.set_field("assoc_handle",associate(ep.uri)->handle()); | ||
127 | }catch(dumb_RP& drp) { } | ||
128 | } OPKELE_RETHROW | ||
129 | if(ext) ext->checkid_hook(rv); | ||
130 | return rv; | ||
131 | } | ||
132 | |||
133 | class signed_part_message_proxy : public basic_openid_message { | ||
134 | public: | ||
135 | const basic_openid_message& x; | ||
136 | set<string> signeds; | ||
137 | |||
138 | signed_part_message_proxy(const basic_openid_message& xx) : x(xx) { | ||
139 | const string& slist = x.get_field("signed"); | ||
140 | string::size_type p = 0; | ||
141 | while(true) { | ||
142 | string::size_type co = slist.find(',',p); | ||
143 | string f = (co==string::npos) | ||
144 | ?slist.substr(p):slist.substr(p,co-p); | ||
145 | signeds.insert(f); | ||
146 | if(co==string::npos) break; | ||
147 | p = co+1; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | bool has_field(const string& n) const { | ||
152 | return signeds.find(n)!=signeds.end() && x.has_field(n); } | ||
153 | const string& get_field(const string& n) const { | ||
154 | if(signeds.find(n)==signeds.end()) | ||
155 | throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed"); | ||
156 | return x.get_field(n); } | ||
157 | |||
158 | fields_iterator fields_begin() const { | ||
159 | return signeds.begin(); } | ||
160 | fields_iterator fields_end() const { | ||
161 | return signeds.end(); } | ||
162 | }; | ||
163 | |||
164 | static void parse_query(const string& u,string::size_type q, | ||
165 | map<string,string>& p) { | ||
166 | if(q==string::npos) | ||
167 | return; | ||
168 | assert(u[q]=='?'); | ||
169 | ++q; | ||
170 | string::size_type l = u.size(); | ||
171 | while(q<l) { | ||
172 | string::size_type eq = u.find('=',q); | ||
173 | string::size_type am = u.find('&',q); | ||
174 | if(am==string::npos) { | ||
175 | if(eq==string::npos) { | ||
176 | p[""] = u.substr(q); | ||
177 | }else{ | ||
178 | p[u.substr(q,eq-q)] = u.substr(eq+1); | ||
179 | } | ||
180 | break; | ||
181 | }else{ | ||
182 | if(eq==string::npos || eq>am) { | ||
183 | p[""] = u.substr(q,eq-q); | ||
184 | }else{ | ||
185 | p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1); | ||
186 | } | ||
187 | q = ++am; | ||
188 | } | ||
189 | } | ||
190 | } | ||
191 | |||
192 | void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) { | ||
193 | bool o2 = om.has_field("ns") | ||
194 | && om.get_field("ns")==OIURI_OPENID20; | ||
195 | if( (!o2) && om.has_field("user_setup_url")) | ||
196 | throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided", | ||
197 | om.get_field("user_setup_url")); | ||
198 | string m = om.get_field("mode"); | ||
199 | if(o2 && m=="setup_needed") | ||
200 | throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided"); | ||
201 | if(m=="cancel") | ||
202 | throw id_res_cancel(OPKELE_CP_ "authentication cancelled"); | ||
203 | bool go_dumb=false; | ||
204 | try { | ||
205 | string OP = o2 | ||
206 | ?om.get_field("op_endpoint") | ||
207 | :get_endpoint().uri; | ||
208 | assoc_t assoc = retrieve_assoc( | ||
209 | OP,om.get_field("assoc_handle")); | ||
210 | if(om.get_field("sig")!=util::base64_signature(assoc,om)) | ||
211 | throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); | ||
212 | }catch(dumb_RP& drp) { | ||
213 | go_dumb=true; | ||
214 | }catch(failed_lookup& e) { | ||
215 | go_dumb=true; | ||
216 | } OPKELE_RETHROW | ||
217 | if(go_dumb) { | ||
218 | try { | ||
219 | string OP = o2 | ||
220 | ?om.get_field("op_endpoint") | ||
221 | :get_endpoint().uri; | ||
222 | check_authentication(OP,om); | ||
223 | }catch(failed_check_authentication& fca) { | ||
224 | throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); | ||
225 | } OPKELE_RETHROW | ||
226 | } | ||
227 | signed_part_message_proxy signeds(om); | ||
228 | if(o2) { | ||
229 | check_nonce(om.get_field("op_endpoint"), | ||
230 | om.get_field("response_nonce")); | ||
231 | static const char *mustsign[] = { | ||
232 | "op_endpoint", "return_to", "response_nonce", "assoc_handle", | ||
233 | "claimed_id", "identity" }; | ||
234 | for(int ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) { | ||
235 | if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms])) | ||
236 | throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs"); | ||
237 | } | ||
238 | if( ( | ||
239 | (om.has_field("claimed_id")?1:0) | ||
240 | ^ | ||
241 | (om.has_field("identity")?1:0) | ||
242 | )&1 ) | ||
243 | throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent"); | ||
244 | |||
245 | string turl = util::rfc_3986_normalize_uri(get_this_url()); | ||
246 | util::strip_uri_fragment_part(turl); | ||
247 | string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to")); | ||
248 | util::strip_uri_fragment_part(rurl); | ||
249 | string::size_type | ||
250 | tq = turl.find('?'), rq = rurl.find('?'); | ||
251 | if( | ||
252 | ((tq==string::npos)?turl:turl.substr(0,tq)) | ||
253 | != | ||
254 | ((rq==string::npos)?rurl:rurl.substr(0,rq)) | ||
255 | ) | ||
256 | throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url"); | ||
257 | map<string,string> tp; parse_query(turl,tq,tp); | ||
258 | map<string,string> rp; parse_query(rurl,rq,rp); | ||
259 | for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) { | ||
260 | map<string,string>::const_iterator tpi = tp.find(rpi->first); | ||
261 | if(tpi==tp.end()) | ||
262 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request"); | ||
263 | if(tpi->second!=rpi->second) | ||
264 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request"); | ||
265 | } | ||
266 | |||
267 | if(om.has_field("claimed_id")) { | ||
268 | verify_OP( | ||
269 | om.get_field("op_endpoint"), | ||
270 | om.get_field("claimed_id"), | ||
271 | om.get_field("identity") ); | ||
272 | } | ||
273 | |||
274 | } | ||
275 | if(ext) ext->id_res_hook(om,signeds); | ||
276 | } | ||
277 | |||
278 | class check_auth_message_proxy : public basic_openid_message { | ||
279 | public: | ||
280 | const basic_openid_message& x; | ||
281 | |||
282 | check_auth_message_proxy(const basic_openid_message& xx) : x(xx) { } | ||
283 | |||
284 | bool has_field(const string& n) const { return x.has_field(n); } | ||
285 | const string& get_field(const string& n) const { | ||
286 | static const string checkauthmode="check_authentication"; | ||
287 | return (n=="mode")?checkauthmode:x.get_field(n); } | ||
288 | bool has_ns(const string& uri) const {return x.has_ns(uri); } | ||
289 | string get_ns(const string& uri) const { return x.get_ns(uri); } | ||
290 | fields_iterator fields_begin() const { | ||
291 | return x.fields_begin(); } | ||
292 | fields_iterator fields_end() const { | ||
293 | return x.fields_end(); } | ||
294 | }; | ||
295 | |||
296 | void basic_RP::check_authentication(const string& OP, | ||
297 | const basic_openid_message& om){ | ||
298 | openid_message_t res; | ||
299 | direct_request(res,check_auth_message_proxy(om),OP); | ||
300 | if(res.has_field("is_valid")) { | ||
301 | if(res.get_field("is_valid")=="true") { | ||
302 | if(res.has_field("invalidate_handle")) | ||
303 | invalidate_assoc(OP,res.get_field("invalidate_handle")); | ||
304 | return; | ||
305 | } | ||
306 | } | ||
307 | throw failed_check_authentication( | ||
308 | OPKELE_CP_ "failed to verify response"); | ||
309 | } | ||
310 | |||
311 | } | ||
diff --git a/lib/consumer.cc b/lib/consumer.cc index 3c3b4f8..ebda262 100644 --- a/lib/consumer.cc +++ b/lib/consumer.cc | |||
@@ -156,3 +156,3 @@ namespace opkele { | |||
156 | } | 156 | } |
157 | if(ext) ext->checkid_hook(p,identity); | 157 | if(ext) ext->checkid_hook(p); |
158 | return p.append_query(server); | 158 | return p.append_query(server); |
@@ -224,3 +224,3 @@ namespace opkele { | |||
224 | } | 224 | } |
225 | if(ext) ext->id_res_hook(pin,ps,identity); | 225 | if(ext) ext->id_res_hook(pin,ps); |
226 | } | 226 | } |
diff --git a/lib/discovery.cc b/lib/discovery.cc index d868308..93409f4 100644 --- a/lib/discovery.cc +++ b/lib/discovery.cc | |||
@@ -20,2 +20,5 @@ namespace opkele { | |||
20 | 20 | ||
21 | /* TODO: the whole discovery thing needs cleanup and optimization due to | ||
22 | * many changes of concept. */ | ||
23 | |||
21 | static const char *whitespace = " \t\r\n"; | 24 | static const char *whitespace = " \t\r\n"; |
@@ -24,2 +27,16 @@ namespace opkele { | |||
24 | 27 | ||
28 | static const struct service_type_t { | ||
29 | const char *uri; | ||
30 | const char *forceid; | ||
31 | } service_types[] = { | ||
32 | { STURI_OPENID20_OP, IDURI_SELECT20 }, | ||
33 | { STURI_OPENID20, 0 }, | ||
34 | { STURI_OPENID11, 0 }, | ||
35 | { STURI_OPENID10, 0 } | ||
36 | }; | ||
37 | enum { | ||
38 | st_index_1 = 2, st_index_2 = 1 | ||
39 | }; | ||
40 | |||
41 | |||
25 | static inline bool is_qelement(const XML_Char *n,const char *qen) { | 42 | static inline bool is_qelement(const XML_Char *n,const char *qen) { |
@@ -50,3 +67,3 @@ namespace opkele { | |||
50 | enum { | 67 | enum { |
51 | xmode_html = 1, xmode_xrd = 2 | 68 | xmode_html = 1, xmode_xrd = 2, xmode_cid = 4 |
52 | }; | 69 | }; |
@@ -86,7 +103,8 @@ namespace opkele { | |||
86 | 103 | ||
87 | void discover(idiscovery_t& result,const string& identity) { | 104 | string discover(endpoint_discovery_iterator& oi,const string& identity) { |
88 | result.clear(); | 105 | string rv; |
106 | idiscovery_t idis; | ||
89 | string::size_type fsc = identity.find_first_not_of(whitespace); | 107 | string::size_type fsc = identity.find_first_not_of(whitespace); |
90 | if(fsc==string::npos) | 108 | if(fsc==string::npos) |
91 | throw bad_input(OPKELE_CP_ "whtiespace-only identity"); | 109 | throw bad_input(OPKELE_CP_ "whitespace-only identity"); |
92 | string::size_type lsc = identity.find_last_not_of(whitespace); | 110 | string::size_type lsc = identity.find_last_not_of(whitespace); |
@@ -98,18 +116,47 @@ namespace opkele { | |||
98 | string id(identity,fsc,lsc-fsc+1); | 116 | string id(identity,fsc,lsc-fsc+1); |
117 | idis.clear(); | ||
99 | if(strchr(i_leaders,id[0])) { | 118 | if(strchr(i_leaders,id[0])) { |
100 | result.normalized_id = id; | 119 | /* TODO: further normalize xri identity? Like folding case |
101 | result.xri_identity = true; | 120 | * or whatever... */ |
102 | /* TODO: further canonicalize xri identity? Like folding case or whatever... */ | 121 | rv = idis.normalized_id = id; |
103 | discover_at( | 122 | idis.xri_identity = true; |
104 | result, | 123 | set<string> cids; |
105 | xri_proxy + util::url_encode(id)+ | 124 | for(const struct service_type_t *st=service_types; |
106 | "?_xrd_r=application/xrd+xml;sep=false", xmode_xrd); | 125 | st<&service_types[sizeof(service_types)/sizeof(*service_types)];++st) { |
107 | if(status_code!=100) | 126 | idis.clear(); |
108 | throw failed_xri_resolution(OPKELE_CP_ | 127 | discover_at( idis, |
109 | "XRI resolution failed with '"+status_string+"' message",status_code); | 128 | xri_proxy + util::url_encode(id)+ |
110 | if(result.xrd.canonical_ids.empty()) | 129 | "?_xrd_t="+util::url_encode(st->uri)+ |
111 | throw opkele::failed_discovery(OPKELE_CP_ "No CanonicalID for XRI identity found"); | 130 | "&_xrd_r=application/xrd%2Bxml" |
112 | result.canonicalized_id = result.xrd.canonical_ids.begin()->second; | 131 | ";sep=true;refs=true", |
132 | xmode_xrd ); | ||
133 | if(status_code==241) continue; | ||
134 | if(status_code!=100) | ||
135 | throw failed_xri_resolution(OPKELE_CP_ | ||
136 | "XRI resolution failed with '"+status_string+"' message" | ||
137 | ", while looking for SEP with type '"+st->uri+"'", status_code); | ||
138 | if(idis.xrd.canonical_ids.empty()) | ||
139 | throw opkele::failed_discovery(OPKELE_CP_ "No CanonicalID found for XRI identity found"); | ||
140 | string cid = idis.xrd.canonical_ids.begin()->second; | ||
141 | if(cids.find(cid)==cids.end()) { | ||
142 | cids.insert(cid); | ||
143 | idis.clear(); | ||
144 | discover_at( idis, | ||
145 | xri_proxy + util::url_encode(id)+ | ||
146 | "?_xrd_t="+util::url_encode(st->uri)+ | ||
147 | "&_xrd_r=application/xrd%2Bxml" | ||
148 | ";sep=true;refs=true", | ||
149 | xmode_xrd ); | ||
150 | if(status_code==241) continue; | ||
151 | if(status_code!=100) | ||
152 | throw failed_xri_resolution(OPKELE_CP_ | ||
153 | "XRI resolution failed with '"+status_string+"' message" | ||
154 | ", while looking for SEP with type '"+st->uri+"'" | ||
155 | " on canonical id", status_code); | ||
156 | } | ||
157 | idis.canonicalized_id = cid; | ||
158 | queue_endpoints(oi,idis,st); | ||
159 | } | ||
113 | }else{ | 160 | }else{ |
114 | result.xri_identity = false; | 161 | idis.xri_identity = false; |
115 | if(id.find("://")==string::npos) | 162 | if(id.find("://")==string::npos) |
@@ -124,4 +171,4 @@ namespace opkele { | |||
124 | } | 171 | } |
125 | result.normalized_id = util::rfc_3986_normalize_uri(id); | 172 | rv = idis.normalized_id = util::rfc_3986_normalize_uri(id); |
126 | discover_at(result,id,xmode_html|xmode_xrd); | 173 | discover_at(idis,id,xmode_html|xmode_xrd); |
127 | const char * eu = 0; | 174 | const char * eu = 0; |
@@ -130,14 +177,23 @@ namespace opkele { | |||
130 | throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r); | 177 | throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r); |
131 | result.canonicalized_id = util::rfc_3986_normalize_uri(eu); /* XXX: strip fragment part? */ | 178 | string cid = util::strip_uri_fragment_part( idis.canonicalized_id = util::rfc_3986_normalize_uri(eu) ); |
132 | if(xrds_location.empty()) { | 179 | if(xrds_location.empty()) { |
133 | html2xrd(result.xrd); | 180 | html2xrd(oi,idis); |
134 | }else{ | 181 | }else{ |
135 | discover_at(result,xrds_location,xmode_xrd); | 182 | idis.clear(); |
136 | if(result.xrd.empty()) | 183 | idis.canonicalized_id = cid; |
137 | html2xrd(result.xrd); | 184 | discover_at(idis,xrds_location,xmode_xrd); |
185 | if(idis.xrd.empty()) | ||
186 | html2xrd(oi,idis); | ||
187 | else{ | ||
188 | for(const service_type_t *st=service_types; | ||
189 | st<&service_types[sizeof(service_types)/sizeof(*service_types)];++st) | ||
190 | queue_endpoints(oi,idis,st); | ||
191 | } | ||
138 | } | 192 | } |
139 | } | 193 | } |
194 | return rv; | ||
140 | } | 195 | } |
141 | 196 | ||
142 | void discover_at(idiscovery_t& result,const string& url,int xm) { | 197 | void discover_at(idiscovery_t& idis,const string& url,int xm) { |
198 | DOUT_("Doing discovery at " << url); | ||
143 | CURLcode r = easy_setopt(CURLOPT_URL,url.c_str()); | 199 | CURLcode r = easy_setopt(CURLOPT_URL,url.c_str()); |
@@ -154,3 +210,3 @@ namespace opkele { | |||
154 | } | 210 | } |
155 | xrd = &result.xrd; | 211 | xrd = &idis.xrd; |
156 | 212 | ||
@@ -201,2 +257,3 @@ namespace opkele { | |||
201 | cdata = 0; xrd_service = 0; skipping = 0; | 257 | cdata = 0; xrd_service = 0; skipping = 0; |
258 | pt_stack.clear(); | ||
202 | status_code = 100; status_string.clear(); | 259 | status_code = 100; status_string.clear(); |
@@ -204,7 +261,4 @@ namespace opkele { | |||
204 | 261 | ||
205 | void html2xrd(XRD_t& x) { | 262 | void html2xrd(endpoint_discovery_iterator& oi,idiscovery_t& id) { |
206 | if(!html_openid1.uris.empty()) { | 263 | XRD_t& x = id.xrd; |
207 | html_openid1.types.insert(STURI_OPENID11); | ||
208 | x.services.add(-1,html_openid1); | ||
209 | } | ||
210 | if(!html_openid2.uris.empty()) { | 264 | if(!html_openid2.uris.empty()) { |
@@ -212,2 +266,8 @@ namespace opkele { | |||
212 | x.services.add(-1,html_openid2); | 266 | x.services.add(-1,html_openid2); |
267 | queue_endpoints(oi,id,&service_types[st_index_2]); | ||
268 | } | ||
269 | if(!html_openid1.uris.empty()) { | ||
270 | html_openid1.types.insert(STURI_OPENID11); | ||
271 | x.services.add(-1,html_openid1); | ||
272 | queue_endpoints(oi,id,&service_types[st_index_1]); | ||
213 | } | 273 | } |
@@ -312,3 +372,4 @@ namespace opkele { | |||
312 | } | 372 | } |
313 | } | 373 | }else |
374 | ++a; | ||
314 | } | 375 | } |
@@ -438,7 +499,37 @@ namespace opkele { | |||
438 | 499 | ||
500 | void queue_endpoints(endpoint_discovery_iterator& oi, | ||
501 | const idiscovery_t &id, | ||
502 | const service_type_t *st) { | ||
503 | openid_endpoint_t ep; | ||
504 | ep.claimed_id = id.canonicalized_id; | ||
505 | for(xrd::services_t::const_iterator isvc=id.xrd.services.begin(); | ||
506 | isvc!=id.xrd.services.end(); ++isvc) { | ||
507 | const xrd::service_t svc = isvc->second; | ||
508 | if(svc.types.find(st->uri)==svc.types.end()) continue; | ||
509 | for(xrd::uris_t::const_iterator iu=svc.uris.begin();iu!=svc.uris.end();++iu) { | ||
510 | ep.uri = iu->second; | ||
511 | if(st->forceid) { | ||
512 | ep.local_id = ep.claimed_id = st->forceid; | ||
513 | *(oi++) = ep; | ||
514 | }else{ | ||
515 | if(svc.local_ids.empty()) { | ||
516 | ep.local_id = ep.claimed_id; | ||
517 | *(oi++) = ep; | ||
518 | }else{ | ||
519 | for(xrd::local_ids_t::const_iterator ilid=svc.local_ids.begin(); | ||
520 | ilid!=svc.local_ids.end(); ++ilid) { | ||
521 | ep.local_id = ilid->second; | ||
522 | *(oi++) = ep; | ||
523 | } | ||
524 | } | ||
525 | } | ||
526 | } | ||
527 | } | ||
528 | } | ||
529 | |||
439 | }; | 530 | }; |
440 | 531 | ||
441 | void idiscover(idiscovery_t& result,const string& identity) { | 532 | string idiscover(endpoint_discovery_iterator oi,const string& identity) { |
442 | idigger_t idigger; | 533 | idigger_t idigger; |
443 | idigger.discover(result,identity); | 534 | return idigger.discover(oi,identity); |
444 | } | 535 | } |
diff --git a/lib/extension.cc b/lib/extension.cc index 8f22562..6451249 100644 --- a/lib/extension.cc +++ b/lib/extension.cc | |||
@@ -5,9 +5,9 @@ namespace opkele { | |||
5 | 5 | ||
6 | void extension_t::checkid_hook(params_t& /* p */,const string& /* identity */ ) { | 6 | void extension_t::checkid_hook(basic_openid_message&) { |
7 | throw not_implemented(OPKELE_CP_ "Consumer checkid_hook not implemented"); | 7 | throw not_implemented(OPKELE_CP_ "Consumer checkid_hook not implemented"); |
8 | } | 8 | } |
9 | void extension_t::id_res_hook(const params_t& /* p */,const params_t& /* sp */,const string& /* identity */) { | 9 | void extension_t::id_res_hook(const basic_openid_message&,const basic_openid_message&) { |
10 | throw not_implemented(OPKELE_CP_ "Consumer id_res_hook not implemented"); | 10 | throw not_implemented(OPKELE_CP_ "Consumer id_res_hook not implemented"); |
11 | } | 11 | } |
12 | void extension_t::checkid_hook(const params_t& /* pin */,params_t& /* pout */) { | 12 | void extension_t::checkid_hook(const basic_openid_message&,basic_openid_message&) { |
13 | throw not_implemented(OPKELE_CP_ "Server checkid_hook not implemented"); | 13 | throw not_implemented(OPKELE_CP_ "Server checkid_hook not implemented"); |
diff --git a/lib/extension_chain.cc b/lib/extension_chain.cc index 16537dc..5c2afd9 100644 --- a/lib/extension_chain.cc +++ b/lib/extension_chain.cc | |||
@@ -5,10 +5,10 @@ namespace opkele { | |||
5 | 5 | ||
6 | void extension_chain_t::checkid_hook(params_t& p,const string& identity) { | 6 | void extension_chain_t::checkid_hook(basic_openid_message& om){ |
7 | for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(p,identity); | 7 | for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(om); |
8 | } | 8 | } |
9 | void extension_chain_t::id_res_hook(const params_t& p,const params_t& sp,const string& identity) { | 9 | void extension_chain_t::id_res_hook(const basic_openid_message& om,const basic_openid_message& sp) { |
10 | for(iterator i=begin();i!=end();++i) (*i)->id_res_hook(p,sp,identity); | 10 | for(iterator i=begin();i!=end();++i) (*i)->id_res_hook(om,sp); |
11 | } | 11 | } |
12 | void extension_chain_t::checkid_hook(const params_t& pin,params_t& pout) { | 12 | void extension_chain_t::checkid_hook(const basic_openid_message& inm,basic_openid_message& oum) { |
13 | for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(pin,pout); | 13 | for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(inm,oum); |
14 | } | 14 | } |
diff --git a/lib/openid_message.cc b/lib/openid_message.cc new file mode 100644 index 0000000..3b08748 --- a/dev/null +++ b/lib/openid_message.cc | |||
@@ -0,0 +1,228 @@ | |||
1 | #include <cassert> | ||
2 | #include <opkele/types.h> | ||
3 | #include <opkele/exception.h> | ||
4 | #include <opkele/util.h> | ||
5 | #include <opkele/debug.h> | ||
6 | |||
7 | #include "config.h" | ||
8 | |||
9 | namespace opkele { | ||
10 | using std::input_iterator_tag; | ||
11 | using std::unary_function; | ||
12 | |||
13 | struct __om_copier : public unary_function<const string&,void> { | ||
14 | public: | ||
15 | const basic_openid_message& from; | ||
16 | basic_openid_message& to; | ||
17 | |||
18 | __om_copier(basic_openid_message& to,const basic_openid_message& from) | ||
19 | : from(from), to(to) { | ||
20 | to.reset_fields(); | ||
21 | } | ||
22 | |||
23 | result_type operator()(argument_type f) { | ||
24 | to.set_field(f,from.get_field(f)); } | ||
25 | }; | ||
26 | |||
27 | basic_openid_message::basic_openid_message(const basic_openid_message& x) { | ||
28 | x.copy_to(*this); | ||
29 | } | ||
30 | void basic_openid_message::copy_to(basic_openid_message& x) const { | ||
31 | for_each(fields_begin(),fields_end(), | ||
32 | __om_copier(x,*this) ); | ||
33 | } | ||
34 | |||
35 | struct __om_ns_finder : public unary_function<const string&,bool> { | ||
36 | public: | ||
37 | const basic_openid_message& om; | ||
38 | const string& uri; | ||
39 | |||
40 | __om_ns_finder(const basic_openid_message& om, | ||
41 | const string& uri) : om(om), uri(uri) { } | ||
42 | |||
43 | result_type operator()(argument_type f) { | ||
44 | return | ||
45 | (!strncmp(f.c_str(),"ns.",sizeof("ns.")-1)) | ||
46 | && om.get_field(f)==uri ; | ||
47 | } | ||
48 | }; | ||
49 | |||
50 | bool basic_openid_message::has_ns(const string& uri) const { | ||
51 | fields_iterator ei = fields_end(); | ||
52 | fields_iterator i = find_if(fields_begin(),fields_end(), | ||
53 | __om_ns_finder(*this,uri)); | ||
54 | return !(i==ei); | ||
55 | } | ||
56 | string basic_openid_message::get_ns(const string& uri) const { | ||
57 | fields_iterator ei = fields_end(); | ||
58 | fields_iterator i = find_if(fields_begin(),fields_end(), | ||
59 | __om_ns_finder(*this,uri)); | ||
60 | if(i==ei) | ||
61 | throw failed_lookup(OPKELE_CP_ string("failed to find namespace ")+uri); | ||
62 | return i->substr(3); | ||
63 | } | ||
64 | |||
65 | struct __om_query_builder : public unary_function<const string&,void> { | ||
66 | public: | ||
67 | const basic_openid_message& om; | ||
68 | string& rv; | ||
69 | bool first; | ||
70 | |||
71 | __om_query_builder(string& rv,const basic_openid_message& om) | ||
72 | : om(om), first(true), rv(rv) { | ||
73 | for_each(om.fields_begin(),om.fields_end(),*this); | ||
74 | } | ||
75 | __om_query_builder(string& rv,const basic_openid_message& om,const string& url) | ||
76 | : om(om), first(true), rv(rv) { | ||
77 | rv = url; | ||
78 | if(rv.find('?')==string::npos) | ||
79 | rv += '?'; | ||
80 | else | ||
81 | first = false; | ||
82 | for_each(om.fields_begin(),om.fields_end(),*this); | ||
83 | } | ||
84 | |||
85 | result_type operator()(argument_type f) { | ||
86 | if(first) | ||
87 | first = false; | ||
88 | else | ||
89 | rv += '&'; | ||
90 | rv += "openid."; rv+= f; | ||
91 | rv += '='; | ||
92 | rv += util::url_encode(om.get_field(f)); | ||
93 | } | ||
94 | }; | ||
95 | |||
96 | string basic_openid_message::append_query(const string& url) const { | ||
97 | string rv; | ||
98 | return __om_query_builder(rv,*this,url).rv; | ||
99 | } | ||
100 | string basic_openid_message::query_string() const { | ||
101 | string rv; | ||
102 | return __om_query_builder(rv,*this).rv; | ||
103 | } | ||
104 | |||
105 | void basic_openid_message::reset_fields() { | ||
106 | throw not_implemented(OPKELE_CP_ "reset_fields() not implemented"); | ||
107 | } | ||
108 | void basic_openid_message::set_field(const string& n,const string& v) { | ||
109 | throw not_implemented(OPKELE_CP_ "set_field() not implemented"); | ||
110 | } | ||
111 | void basic_openid_message::reset_field(const string& n) { | ||
112 | throw not_implemented(OPKELE_CP_ "reset_field() not implemented"); | ||
113 | } | ||
114 | |||
115 | void basic_openid_message::from_keyvalues(const string& kv) { | ||
116 | reset_fields(); | ||
117 | string::size_type p = 0; | ||
118 | while(true) { | ||
119 | string::size_type co = kv.find(':',p); | ||
120 | if(co==string::npos) | ||
121 | break; | ||
122 | #ifndef POSTELS_LAW | ||
123 | string::size_type nl = kv.find('\n',co+1); | ||
124 | if(nl==string::npos) | ||
125 | throw bad_input(OPKELE_CP_ "malformed input"); | ||
126 | if(nl>co) | ||
127 | insert(value_type(kv.substr(p,co-p),kv.substr(co+1,nl-co-1))); | ||
128 | p = nl+1; | ||
129 | #else /* POSTELS_LAW */ | ||
130 | string::size_type lb = kv.find_first_of("\r\n",co+1); | ||
131 | if(lb==string::npos) { | ||
132 | set_field(kv.substr(p,co-p),kv.substr(co+1)); | ||
133 | break; | ||
134 | } | ||
135 | if(lb>co) | ||
136 | set_field(kv.substr(p,co-p),kv.substr(co+1,lb-co-1)); | ||
137 | string::size_type nolb = kv.find_first_not_of("\r\n",lb); | ||
138 | if(nolb==string::npos) | ||
139 | break; | ||
140 | p = nolb; | ||
141 | #endif /* POSTELS_LAW */ | ||
142 | } | ||
143 | } | ||
144 | |||
145 | void basic_openid_message::add_to_signed(const string& fields) { | ||
146 | string::size_type fnc = fields.find_first_not_of(","); | ||
147 | if(fnc==string::npos) | ||
148 | throw bad_input(OPKELE_CP_ "Trying to add nothing in particular to the list of signed fields"); | ||
149 | string signeds; | ||
150 | try { | ||
151 | signeds = get_field("signed"); | ||
152 | string::size_type lnc = signeds.find_last_not_of(","); | ||
153 | if(lnc==string::npos) | ||
154 | signeds.assign(fields,fnc,fields.size()-fnc); | ||
155 | else{ | ||
156 | string::size_type ss = signeds.size(); | ||
157 | if(lnc==(ss-1)) { | ||
158 | signeds+= ','; | ||
159 | signeds.append(fields,fnc,fields.size()-fnc); | ||
160 | }else{ | ||
161 | if(lnc<(ss-2)) | ||
162 | signeds.replace(lnc+2,ss-lnc-2, | ||
163 | fields,fnc,fields.size()-fnc); | ||
164 | else | ||
165 | signeds.append(fields,fnc,fields.size()-fnc); | ||
166 | } | ||
167 | } | ||
168 | }catch(failed_lookup&) { | ||
169 | signeds.assign(fields,fnc,fields.size()-fnc); | ||
170 | } | ||
171 | set_field("signed",signeds); | ||
172 | } | ||
173 | |||
174 | string basic_openid_message::find_ns(const string& uri,const char *pfx) const { | ||
175 | if(has_field("ns")) | ||
176 | return get_ns(uri); | ||
177 | return pfx; | ||
178 | } | ||
179 | string basic_openid_message::allocate_ns(const string& uri,const char *pfx) { | ||
180 | if(!has_field("ns")) | ||
181 | return pfx; | ||
182 | if(has_ns(uri)) | ||
183 | throw bad_input(OPKELE_CP_ "OpenID message already contains namespace"); | ||
184 | string rv = pfx; | ||
185 | if(has_field("ns."+rv)) { | ||
186 | string::reference c=rv[rv.length()]; | ||
187 | for(c='a';c<='z' && has_field("ns."+rv);++c); | ||
188 | if(c=='z') | ||
189 | throw exception(OPKELE_CP_ "Failed to allocate namespace"); | ||
190 | } | ||
191 | set_field("ns."+rv,uri); | ||
192 | return rv; | ||
193 | } | ||
194 | |||
195 | void openid_message_t::copy_to(basic_openid_message& x) const { | ||
196 | x.reset_fields(); | ||
197 | for(const_iterator i=begin();i!=end();++i) | ||
198 | x.set_field(i->first,i->second); | ||
199 | } | ||
200 | |||
201 | bool openid_message_t::has_field(const string& n) const { | ||
202 | return find(n)!=end(); | ||
203 | } | ||
204 | const string& openid_message_t::get_field(const string& n) const { | ||
205 | const_iterator i=find(n); | ||
206 | if(i==end()) | ||
207 | throw failed_lookup(OPKELE_CP_ n+": no such field"); | ||
208 | return i->second; | ||
209 | } | ||
210 | |||
211 | openid_message_t::fields_iterator openid_message_t::fields_begin() const { | ||
212 | return util::map_keys_iterator<const_iterator,string,const string&,const string*>(begin(),end()); | ||
213 | } | ||
214 | openid_message_t::fields_iterator openid_message_t::fields_end() const { | ||
215 | return util::map_keys_iterator<const_iterator,string,const string&,const string*>(end(),end()); | ||
216 | } | ||
217 | |||
218 | void openid_message_t::reset_fields() { | ||
219 | clear(); | ||
220 | } | ||
221 | void openid_message_t::set_field(const string& n,const string& v) { | ||
222 | insert(value_type(n,v)); | ||
223 | } | ||
224 | void openid_message_t::reset_field(const string& n) { | ||
225 | erase(n); | ||
226 | } | ||
227 | |||
228 | } | ||
diff --git a/lib/params.cc b/lib/params.cc index 7a572c1..6805516 100644 --- a/lib/params.cc +++ b/lib/params.cc | |||
@@ -11,72 +11,2 @@ namespace opkele { | |||
11 | 11 | ||
12 | bool params_t::has_param(const string& n) const { | ||
13 | return find(n)!=end(); | ||
14 | } | ||
15 | const string& params_t::get_param(const string& n) const { | ||
16 | const_iterator i = find(n); | ||
17 | if(i==end()) | ||
18 | throw failed_lookup(OPKELE_CP_ n+": no such parameter"); | ||
19 | return i->second; | ||
20 | } | ||
21 | string& params_t::get_param(const string& n) { | ||
22 | iterator i = find(n); | ||
23 | if(i==end()) | ||
24 | throw failed_lookup(OPKELE_CP_ n+": no such parameter"); | ||
25 | return i->second; | ||
26 | } | ||
27 | |||
28 | void params_t::parse_keyvalues(const string& kv) { | ||
29 | clear(); | ||
30 | string::size_type p = 0; | ||
31 | while(true) { | ||
32 | string::size_type co = kv.find(':',p); | ||
33 | if(co==string::npos) | ||
34 | break; | ||
35 | #ifndef POSTELS_LAW | ||
36 | string::size_type nl = kv.find('\n',co+1); | ||
37 | if(nl==string::npos) | ||
38 | throw bad_input(OPKELE_CP_ "malformed input"); | ||
39 | if(nl>co) | ||
40 | insert(value_type(kv.substr(p,co-p),kv.substr(co+1,nl-co-1))); | ||
41 | p = nl+1; | ||
42 | #else /* POSTELS_LAW */ | ||
43 | string::size_type lb = kv.find_first_of("\r\n",co+1); | ||
44 | if(lb==string::npos) { | ||
45 | insert(value_type(kv.substr(p,co-p),kv.substr(co+1))); | ||
46 | break; | ||
47 | } | ||
48 | if(lb>co) | ||
49 | insert(value_type(kv.substr(p,co-p),kv.substr(co+1,lb-co-1))); | ||
50 | string::size_type nolb = kv.find_first_not_of("\r\n",lb); | ||
51 | if(nolb==string::npos) | ||
52 | break; | ||
53 | p = nolb; | ||
54 | #endif /* POSTELS_LAW */ | ||
55 | } | ||
56 | } | ||
57 | |||
58 | void params_t::sign(secret_t secret,string& sig,const string& slist,const char *prefix) const { | ||
59 | string kv; | ||
60 | string::size_type p = 0; | ||
61 | while(true) { | ||
62 | string::size_type co = slist.find(',',p); | ||
63 | string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p); | ||
64 | kv += f; | ||
65 | kv += ':'; | ||
66 | if(prefix) f.insert(0,prefix); | ||
67 | kv += get_param(f); | ||
68 | kv += '\n'; | ||
69 | if(co==string::npos) | ||
70 | break; | ||
71 | p = co+1; | ||
72 | } | ||
73 | unsigned int md_len = 0; | ||
74 | unsigned char *md = HMAC( | ||
75 | EVP_sha1(), | ||
76 | &(secret.front()),secret.size(), | ||
77 | (const unsigned char *)kv.data(),kv.length(), | ||
78 | 0,&md_len); | ||
79 | sig = util::encode_base64(md,md_len); | ||
80 | } | ||
81 | |||
82 | string params_t::append_query(const string& url,const char *prefix) const { | 12 | string params_t::append_query(const string& url,const char *prefix) const { |
@@ -85,6 +15,4 @@ namespace opkele { | |||
85 | if(rv.find('?')==string::npos) { | 15 | if(rv.find('?')==string::npos) { |
86 | rv += '?'; | 16 | rv += '?'; p = false; } |
87 | p = false; | 17 | for(fields_iterator i=fields_begin();i!=fields_end();++i) { |
88 | } | ||
89 | for(const_iterator i=begin();i!=end();++i) { | ||
90 | if(p) | 18 | if(p) |
@@ -93,6 +21,6 @@ namespace opkele { | |||
93 | p = true; | 21 | p = true; |
94 | rv += prefix; | 22 | if(prefix) rv += prefix; |
95 | rv += i->first; | 23 | rv += *i; |
96 | rv += '='; | 24 | rv += '='; |
97 | rv += util::url_encode(i->second); | 25 | rv += util::url_encode(get_field(*i)); |
98 | } | 26 | } |
@@ -101,21 +29,2 @@ namespace opkele { | |||
101 | 29 | ||
102 | string params_t::query_string(const char *prefix) const { | ||
103 | string rv; | ||
104 | for(const_iterator i=begin();i!=end();++i) { | ||
105 | if(!rv.empty()) | ||
106 | rv += '&'; | ||
107 | rv += prefix; | ||
108 | rv += i->first; | ||
109 | rv += '='; | ||
110 | rv += util::url_encode(i->second); | ||
111 | } | ||
112 | return rv; | ||
113 | } | ||
114 | |||
115 | ostream& operator << (ostream& o,const params_t& p) { | ||
116 | for(params_t::const_iterator i=p.begin();i!=p.end();++i) | ||
117 | o << i->first << ':' << i->second << '\n'; | ||
118 | return o; | ||
119 | } | ||
120 | |||
121 | } | 30 | } |
diff --git a/lib/prequeue_rp.cc b/lib/prequeue_rp.cc new file mode 100644 index 0000000..e242f87 --- a/dev/null +++ b/lib/prequeue_rp.cc | |||
@@ -0,0 +1,81 @@ | |||
1 | #include <iostream> | ||
2 | #include <openssl/sha.h> | ||
3 | #include <openssl/hmac.h> | ||
4 | #include <opkele/exception.h> | ||
5 | #include <opkele/prequeue_rp.h> | ||
6 | #include <opkele/discovery.h> | ||
7 | #include <opkele/uris.h> | ||
8 | #include <opkele/data.h> | ||
9 | #include <opkele/util.h> | ||
10 | #include <opkele/curl.h> | ||
11 | #include <opkele/debug.h> | ||
12 | |||
13 | namespace opkele { | ||
14 | |||
15 | class __OP_verifier_good_input : public exception { | ||
16 | public: | ||
17 | __OP_verifier_good_input(OPKELE_E_PARS) | ||
18 | : exception(OPKELE_E_CONS) { } | ||
19 | }; | ||
20 | |||
21 | class OP_verifier : public iterator<output_iterator_tag,openid_endpoint_t,void> { | ||
22 | public: | ||
23 | const string& OP; | ||
24 | const string& id; | ||
25 | |||
26 | OP_verifier(const string& o,const string& i) | ||
27 | : OP(o), id(i) { } | ||
28 | |||
29 | OP_verifier& operator*() { return *this; } | ||
30 | OP_verifier& operator=(const openid_endpoint_t& oep) { | ||
31 | if(oep.uri==OP) { | ||
32 | if(oep.claimed_id==IDURI_SELECT20 | ||
33 | || oep.local_id==IDURI_SELECT20 ) | ||
34 | throw bad_input(OPKELE_CP_ "claimed_id is an OP-Id"); | ||
35 | if(oep.local_id==id) | ||
36 | throw __OP_verifier_good_input(OPKELE_CP_ "Found corresponding endpoint"); | ||
37 | } | ||
38 | return *this; | ||
39 | } | ||
40 | |||
41 | OP_verifier& operator++() { return *this; } | ||
42 | OP_verifier& operator++(int) { return *this; } | ||
43 | }; | ||
44 | |||
45 | void prequeue_RP::verify_OP(const string& OP,const string& claimed_id,const string& identity) const { | ||
46 | try { | ||
47 | idiscover(OP_verifier(OP,identity),claimed_id); | ||
48 | throw id_res_unauthorized(OPKELE_CP_ | ||
49 | "OP is not authorized to make an assertion regarding the identity"); | ||
50 | }catch(__OP_verifier_good_input& ovgi) { | ||
51 | } | ||
52 | } | ||
53 | |||
54 | class endpoint_queuer : public iterator<output_iterator_tag,openid_endpoint_t,void> { | ||
55 | public: | ||
56 | prequeue_RP& rp; | ||
57 | |||
58 | endpoint_queuer(prequeue_RP& rp) : rp(rp) { } | ||
59 | |||
60 | endpoint_queuer& operator*() { return *this; } | ||
61 | endpoint_queuer& operator=(const openid_endpoint_t& oep) { | ||
62 | rp.queue_endpoint(oep); return *this; } | ||
63 | |||
64 | endpoint_queuer& operator++() { return *this; } | ||
65 | endpoint_queuer& operator++(int) { return *this; } | ||
66 | }; | ||
67 | |||
68 | void prequeue_RP::initiate(const string& usi) { | ||
69 | begin_queueing(); | ||
70 | set_normalized_id( idiscover(endpoint_queuer(*this),usi) ); | ||
71 | end_queueing(); | ||
72 | } | ||
73 | |||
74 | void prequeue_RP::set_normalized_id(const string& nid) { | ||
75 | } | ||
76 | |||
77 | const string prequeue_RP::get_normalized_id() const { | ||
78 | throw not_implemented(OPKELE_CP_ "get_normalized_id() is not implemented"); | ||
79 | } | ||
80 | |||
81 | } | ||
diff --git a/lib/server.cc b/lib/server.cc index 282521e..776f1ae 100644 --- a/lib/server.cc +++ b/lib/server.cc | |||
@@ -111,3 +111,3 @@ namespace opkele { | |||
111 | if(ext) ext->checkid_hook(pin,pout); | 111 | if(ext) ext->checkid_hook(pin,pout); |
112 | pout.sign(assoc->secret(),pout["sig"],pout["signed"]); | 112 | pout["sig"] = util::base64_signature(assoc,pout); |
113 | } | 113 | } |
diff --git a/lib/sreg.cc b/lib/sreg.cc index 03edf57..7e2d588 100644 --- a/lib/sreg.cc +++ b/lib/sreg.cc | |||
@@ -30,3 +30,3 @@ namespace opkele { | |||
30 | 30 | ||
31 | void sreg_t::checkid_hook(params_t& p,const string& /* identity */) { | 31 | void sreg_t::checkid_hook(basic_openid_message& om) { |
32 | string fr, fo; | 32 | string fr, fo; |
@@ -42,15 +42,26 @@ namespace opkele { | |||
42 | } | 42 | } |
43 | p["ns.sreg"] = OIURI_SREG11; | 43 | string pfx = om.allocate_ns(OIURI_SREG11,"sreg"); |
44 | if(!fr.empty()) p["sreg.required"]=fr; | 44 | if(!fr.empty()) om.set_field(pfx+".required",fr); |
45 | if(!fo.empty()) p["sreg.optional"]=fo; | 45 | if(!fo.empty()) om.set_field(pfx+".optional",fo); |
46 | if(!policy_url.empty()) p["sreg.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 params_t& /* p */,const params_t& sp,const string& /* identity */) { | 49 | void sreg_t::id_res_hook(const basic_openid_message& om,const basic_openid_message& sp) { |
50 | clear(); | 50 | clear(); |
51 | string pfx; | ||
52 | try { | ||
53 | pfx = om.find_ns(OIURI_SREG11,"sreg"); | ||
54 | }catch(failed_lookup& fl) { | ||
55 | try { | ||
56 | pfx = om.find_ns(OIURI_SREG10,"sreg"); | ||
57 | }catch(failed_lookup& fl) { | ||
58 | return; | ||
59 | } | ||
60 | } | ||
61 | pfx += '.'; | ||
51 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { | 62 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { |
52 | string fn = "sreg."; fn+=f->fieldname; | 63 | string fn = pfx; fn+=f->fieldname; |
53 | if(!sp.has_param(fn)) continue; | 64 | if(!sp.has_field(fn)) continue; |
54 | has_fields |= f->fieldbit; | 65 | has_fields |= f->fieldbit; |
55 | response[f->fieldbit]=sp.get_param(fn); | 66 | response[f->fieldbit]=sp.get_field(fn); |
56 | } | 67 | } |
@@ -96,3 +107,4 @@ namespace opkele { | |||
96 | 107 | ||
97 | void sreg_t::checkid_hook(const params_t& pin,params_t& pout) { | 108 | void sreg_t::checkid_hook(const basic_openid_message& inm,basic_openid_message& oum) { |
109 | string ins = inm.find_ns(OIURI_SREG11,"sreg"); | ||
98 | fields_optional = 0; fields_required = 0; policy_url.erase(); | 110 | fields_optional = 0; fields_required = 0; policy_url.erase(); |
@@ -100,3 +112,3 @@ namespace opkele { | |||
100 | try { | 112 | try { |
101 | string fl = pin.get_param("openid.sreg.required"); | 113 | string fl = inm.get_field(ins+".required"); |
102 | fields_required = fields_list_to_bitmask(fl); | 114 | fields_required = fields_list_to_bitmask(fl); |
@@ -104,3 +116,3 @@ namespace opkele { | |||
104 | try { | 116 | try { |
105 | string fl = pin.get_param("openid.sreg.optional"); | 117 | string fl = inm.get_field(ins+".optional"); |
106 | fields_optional = fields_list_to_bitmask(fl); | 118 | fields_optional = fields_list_to_bitmask(fl); |
@@ -108,17 +120,19 @@ namespace opkele { | |||
108 | try { | 120 | try { |
109 | policy_url = pin.get_param("openid.sreg.policy_url"); | 121 | policy_url = inm.get_field(ins+".policy_url"); |
110 | }catch(failed_lookup&) { } | 122 | }catch(failed_lookup&) { } |
111 | setup_response(pin,pout); | 123 | setup_response(inm,oum); |
124 | string ons = oum.allocate_ns(OIURI_SREG11,"sreg"); | ||
112 | fields_response &= has_fields; | 125 | fields_response &= has_fields; |
126 | string signeds = "ns."+ons; | ||
113 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { | 127 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { |
114 | if(!(f->fieldbit&fields_response)) continue; | 128 | if(!(f->fieldbit&fields_response)) continue; |
115 | if(!pout["signed"].empty()) | 129 | signeds +=','; |
116 | pout["signed"] +=','; | 130 | string pn = ons; pn += '.'; pn += f->fieldname; |
117 | string pn = "sreg."; pn += f->fieldname; | 131 | signeds += pn; |
118 | pout["signed"] += pn; | 132 | oum.set_field(pn,get_field(f->fieldbit)); |
119 | pout[pn] = get_field(f->fieldbit); | ||
120 | } | 133 | } |
134 | oum.add_to_signed(signeds); | ||
121 | } | 135 | } |
122 | 136 | ||
123 | void sreg_t::setup_response(const params_t& /* pin */,params_t& /* pout */) { | 137 | void sreg_t::setup_response(const basic_openid_message& /* inm */,basic_openid_message& /* oum */) { |
124 | fields_response = (fields_required|fields_optional)&has_fields; | 138 | fields_response = (fields_required|fields_optional)&has_fields; |
diff --git a/lib/util.cc b/lib/util.cc index a9b9bed..54d6535 100644 --- a/lib/util.cc +++ b/lib/util.cc | |||
@@ -9,2 +9,3 @@ | |||
9 | #include <openssl/evp.h> | 9 | #include <openssl/evp.h> |
10 | #include <openssl/hmac.h> | ||
10 | #include <curl/curl.h> | 11 | #include <curl/curl.h> |
@@ -13,2 +14,7 @@ | |||
13 | 14 | ||
15 | #include <config.h> | ||
16 | #ifdef HAVE_DEMANGLE | ||
17 | # include <cxxabi.h> | ||
18 | #endif | ||
19 | |||
14 | namespace opkele { | 20 | namespace opkele { |
@@ -207,4 +213,3 @@ namespace opkele { | |||
207 | else{ | 213 | else{ |
208 | /* TODO: support more schemes. | 214 | /* TODO: support more schemes. e.g. xri. How do we normalize |
209 | * e.g. xri. How do we normalize | ||
210 | * xri? | 215 | * xri? |
@@ -313,2 +318,64 @@ namespace opkele { | |||
313 | 318 | ||
319 | string& strip_uri_fragment_part(string& u) { | ||
320 | string::size_type q = u.find('?'), f = u.find('#'); | ||
321 | if(q==string::npos) { | ||
322 | if(f!=string::npos) | ||
323 | u.erase(f); | ||
324 | }else{ | ||
325 | if(f!=string::npos) { | ||
326 | if(f<q) | ||
327 | u.erase(f,q-f); | ||
328 | else | ||
329 | u.erase(f); | ||
330 | } | ||
331 | } | ||
332 | return u; | ||
333 | } | ||
334 | |||
335 | string abi_demangle(const char *mn) { | ||
336 | #ifndef HAVE_DEMANGLE | ||
337 | return mn; | ||
338 | #else /* !HAVE_DEMANGLE */ | ||
339 | int dstat; | ||
340 | char *demangled = abi::__cxa_demangle(mn,0,0,&dstat); | ||
341 | if(dstat) | ||
342 | return mn; | ||
343 | string rv = demangled; | ||
344 | free(demangled); | ||
345 | return rv; | ||
346 | #endif /* !HAVE_DEMANGLE */ | ||
347 | } | ||
348 | |||
349 | string base64_signature(const assoc_t& assoc,const basic_openid_message& om) { | ||
350 | const string& slist = om.get_field("signed"); | ||
351 | string kv; | ||
352 | string::size_type p=0; | ||
353 | while(true) { | ||
354 | string::size_type co = slist.find(',',p); | ||
355 | string f = (co==string::npos) | ||
356 | ?slist.substr(p):slist.substr(p,co-p); | ||
357 | kv += f; | ||
358 | kv += ':'; | ||
359 | kv += om.get_field(f); | ||
360 | kv += '\n'; | ||
361 | if(co==string::npos) break; | ||
362 | p = co+1; | ||
363 | } | ||
364 | const secret_t& secret = assoc->secret(); | ||
365 | const EVP_MD *evpmd; | ||
366 | const string& at = assoc->assoc_type(); | ||
367 | if(at=="HMAC-SHA256") | ||
368 | evpmd = EVP_sha256(); | ||
369 | else if(at=="HMAC-SHA1") | ||
370 | evpmd = EVP_sha1(); | ||
371 | else | ||
372 | throw unsupported(OPKELE_CP_ "unknown association type"); | ||
373 | unsigned int md_len = 0; | ||
374 | unsigned char *md = HMAC(evpmd, | ||
375 | &(secret.front()),secret.size(), | ||
376 | (const unsigned char*)kv.data(),kv.length(), | ||
377 | 0,&md_len); | ||
378 | return encode_base64(md,md_len); | ||
379 | } | ||
380 | |||
314 | } | 381 | } |