-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 | |||
@@ -1,31 +1,34 @@ | |||
1 | lib_LTLIBRARIES = libopkele.la | 1 | lib_LTLIBRARIES = libopkele.la |
2 | 2 | ||
3 | AM_CPPFLAGS = ${CPPFLAGS_DEBUG} | 3 | AM_CPPFLAGS = ${CPPFLAGS_DEBUG} |
4 | DEFAULT_INCLUDES = -I${top_builddir} | 4 | 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} \ |
8 | ${OPENSSL_CFLAGS} \ | 8 | ${OPENSSL_CFLAGS} \ |
9 | ${LIBCURL_CPPFLAGS} \ | 9 | ${LIBCURL_CPPFLAGS} \ |
10 | ${PCRE_CFLAGS} ${EXPAT_CFLAGS} ${TIDY_CFLAGS} | 10 | ${PCRE_CFLAGS} ${EXPAT_CFLAGS} ${TIDY_CFLAGS} |
11 | libopkele_la_LIBADD = \ | 11 | libopkele_la_LIBADD = \ |
12 | ${LIBCURL} \ | 12 | ${LIBCURL} \ |
13 | ${PCRE_LIBS} ${EXPAT_LIBS} \ | 13 | ${PCRE_LIBS} ${EXPAT_LIBS} \ |
14 | ${OPENSSL_LIBS} \ | 14 | ${OPENSSL_LIBS} \ |
15 | ${KONFORKA_LIBS} ${TIDY_LIBS} | 15 | ${KONFORKA_LIBS} ${TIDY_LIBS} |
16 | 16 | ||
17 | libopkele_la_SOURCES = \ | 17 | libopkele_la_SOURCES = \ |
18 | params.cc \ | 18 | params.cc \ |
19 | util.cc \ | 19 | util.cc \ |
20 | server.cc \ | 20 | server.cc \ |
21 | secret.cc \ | 21 | secret.cc \ |
22 | data.cc \ | 22 | data.cc \ |
23 | consumer.cc \ | 23 | consumer.cc \ |
24 | exception.cc \ | 24 | exception.cc \ |
25 | extension.cc \ | 25 | extension.cc \ |
26 | sreg.cc \ | 26 | sreg.cc \ |
27 | extension_chain.cc \ | 27 | extension_chain.cc \ |
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 = \ |
31 | -version-info 2:0:0 | 34 | -version-info 2:0:0 |
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 | |||
@@ -1,395 +1,395 @@ | |||
1 | #include <algorithm> | 1 | #include <algorithm> |
2 | #include <cassert> | 2 | #include <cassert> |
3 | #include <cstring> | 3 | #include <cstring> |
4 | #include <opkele/util.h> | 4 | #include <opkele/util.h> |
5 | #include <opkele/curl.h> | 5 | #include <opkele/curl.h> |
6 | #include <opkele/exception.h> | 6 | #include <opkele/exception.h> |
7 | #include <opkele/data.h> | 7 | #include <opkele/data.h> |
8 | #include <opkele/consumer.h> | 8 | #include <opkele/consumer.h> |
9 | #include <openssl/sha.h> | 9 | #include <openssl/sha.h> |
10 | #include <openssl/hmac.h> | 10 | #include <openssl/hmac.h> |
11 | #include <iostream> | 11 | #include <iostream> |
12 | 12 | ||
13 | #include "config.h" | 13 | #include "config.h" |
14 | 14 | ||
15 | #include <pcre.h> | 15 | #include <pcre.h> |
16 | 16 | ||
17 | namespace opkele { | 17 | namespace opkele { |
18 | using namespace std; | 18 | using namespace std; |
19 | using util::curl_t; | 19 | using util::curl_t; |
20 | using util::curl_pick_t; | 20 | using util::curl_pick_t; |
21 | 21 | ||
22 | class pcre_matches_t { | 22 | class pcre_matches_t { |
23 | public: | 23 | public: |
24 | int *_ov; | 24 | int *_ov; |
25 | int _s; | 25 | int _s; |
26 | 26 | ||
27 | pcre_matches_t() : _ov(0), _s(0) { } | 27 | pcre_matches_t() : _ov(0), _s(0) { } |
28 | pcre_matches_t(int s) : _ov(0), _s(s) { | 28 | pcre_matches_t(int s) : _ov(0), _s(s) { |
29 | if(_s&1) ++_s; | 29 | if(_s&1) ++_s; |
30 | _s += _s>>1; | 30 | _s += _s>>1; |
31 | _ov = new int[_s]; | 31 | _ov = new int[_s]; |
32 | } | 32 | } |
33 | ~pcre_matches_t() throw() { if(_ov) delete[] _ov; } | 33 | ~pcre_matches_t() throw() { if(_ov) delete[] _ov; } |
34 | 34 | ||
35 | int begin(int i) const { return _ov[i<<1]; } | 35 | int begin(int i) const { return _ov[i<<1]; } |
36 | int end(int i) const { return _ov[(i<<1)+1]; } | 36 | int end(int i) const { return _ov[(i<<1)+1]; } |
37 | int length(int i) const { int t=i<<1; return _ov[t+1]-_ov[t]; } | 37 | int length(int i) const { int t=i<<1; return _ov[t+1]-_ov[t]; } |
38 | }; | 38 | }; |
39 | 39 | ||
40 | class pcre_t { | 40 | class pcre_t { |
41 | public: | 41 | public: |
42 | pcre *_p; | 42 | pcre *_p; |
43 | 43 | ||
44 | pcre_t() : _p(0) { } | 44 | pcre_t() : _p(0) { } |
45 | pcre_t(pcre *p) : _p(p) { } | 45 | pcre_t(pcre *p) : _p(p) { } |
46 | pcre_t(const char *re,int opts) : _p(0) { | 46 | pcre_t(const char *re,int opts) : _p(0) { |
47 | static const char *errptr; static int erroffset; | 47 | static const char *errptr; static int erroffset; |
48 | _p = pcre_compile(re,opts,&errptr,&erroffset,NULL); | 48 | _p = pcre_compile(re,opts,&errptr,&erroffset,NULL); |
49 | if(!_p) | 49 | if(!_p) |
50 | throw internal_error(OPKELE_CP_ string("Failed to compile regexp: ")+errptr); | 50 | throw internal_error(OPKELE_CP_ string("Failed to compile regexp: ")+errptr); |
51 | } | 51 | } |
52 | ~pcre_t() throw() { if(_p) (*pcre_free)(_p); } | 52 | ~pcre_t() throw() { if(_p) (*pcre_free)(_p); } |
53 | 53 | ||
54 | pcre_t& operator=(pcre *p) { if(_p) (*pcre_free)(_p); _p=p; return *this; } | 54 | pcre_t& operator=(pcre *p) { if(_p) (*pcre_free)(_p); _p=p; return *this; } |
55 | 55 | ||
56 | operator const pcre*(void) const { return _p; } | 56 | operator const pcre*(void) const { return _p; } |
57 | operator pcre*(void) { return _p; } | 57 | operator pcre*(void) { return _p; } |
58 | 58 | ||
59 | int exec(const string& s,pcre_matches_t& m) { | 59 | int exec(const string& s,pcre_matches_t& m) { |
60 | if(!_p) | 60 | if(!_p) |
61 | throw internal_error(OPKELE_CP_ "Trying to execute absent regexp"); | 61 | throw internal_error(OPKELE_CP_ "Trying to execute absent regexp"); |
62 | return pcre_exec(_p,NULL,s.c_str(),s.length(),0,0,m._ov,m._s); | 62 | return pcre_exec(_p,NULL,s.c_str(),s.length(),0,0,m._ov,m._s); |
63 | } | 63 | } |
64 | }; | 64 | }; |
65 | 65 | ||
66 | assoc_t consumer_t::associate(const string& server) { | 66 | assoc_t consumer_t::associate(const string& server) { |
67 | util::dh_t dh = DH_new(); | 67 | util::dh_t dh = DH_new(); |
68 | if(!dh) | 68 | if(!dh) |
69 | throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); | 69 | throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); |
70 | dh->p = util::dec_to_bignum(data::_default_p); | 70 | dh->p = util::dec_to_bignum(data::_default_p); |
71 | dh->g = util::dec_to_bignum(data::_default_g); | 71 | dh->g = util::dec_to_bignum(data::_default_g); |
72 | if(!DH_generate_key(dh)) | 72 | if(!DH_generate_key(dh)) |
73 | throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); | 73 | throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); |
74 | string request = | 74 | string request = |
75 | "openid.mode=associate" | 75 | "openid.mode=associate" |
76 | "&openid.assoc_type=HMAC-SHA1" | 76 | "&openid.assoc_type=HMAC-SHA1" |
77 | "&openid.session_type=DH-SHA1" | 77 | "&openid.session_type=DH-SHA1" |
78 | "&openid.dh_consumer_public="; | 78 | "&openid.dh_consumer_public="; |
79 | request += util::url_encode(util::bignum_to_base64(dh->pub_key)); | 79 | request += util::url_encode(util::bignum_to_base64(dh->pub_key)); |
80 | curl_pick_t curl = curl_pick_t::easy_init(); | 80 | curl_pick_t curl = curl_pick_t::easy_init(); |
81 | if(!curl) | 81 | if(!curl) |
82 | throw exception_curl(OPKELE_CP_ "failed to initialize curl"); | 82 | throw exception_curl(OPKELE_CP_ "failed to initialize curl"); |
83 | CURLcode r; | 83 | CURLcode r; |
84 | (r=curl.misc_sets()) | 84 | (r=curl.misc_sets()) |
85 | || (r=curl.easy_setopt(CURLOPT_URL,server.c_str())) | 85 | || (r=curl.easy_setopt(CURLOPT_URL,server.c_str())) |
86 | || (r=curl.easy_setopt(CURLOPT_POST,1)) | 86 | || (r=curl.easy_setopt(CURLOPT_POST,1)) |
87 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) | 87 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) |
88 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) | 88 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) |
89 | || (r=curl.set_write()) | 89 | || (r=curl.set_write()) |
90 | ; | 90 | ; |
91 | if(r) | 91 | if(r) |
92 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); | 92 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); |
93 | if( (r=curl.easy_perform()) ) | 93 | if( (r=curl.easy_perform()) ) |
94 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); | 94 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); |
95 | params_t p; p.parse_keyvalues(curl.response); | 95 | params_t p; p.parse_keyvalues(curl.response); |
96 | if(p.has_param("assoc_type") && p.get_param("assoc_type")!="HMAC-SHA1") | 96 | if(p.has_param("assoc_type") && p.get_param("assoc_type")!="HMAC-SHA1") |
97 | throw bad_input(OPKELE_CP_ "unsupported assoc_type"); | 97 | throw bad_input(OPKELE_CP_ "unsupported assoc_type"); |
98 | string st; | 98 | string st; |
99 | if(p.has_param("session_type")) st = p.get_param("session_type"); | 99 | if(p.has_param("session_type")) st = p.get_param("session_type"); |
100 | if((!st.empty()) && st!="DH-SHA1") | 100 | if((!st.empty()) && st!="DH-SHA1") |
101 | throw bad_input(OPKELE_CP_ "unsupported session_type"); | 101 | throw bad_input(OPKELE_CP_ "unsupported session_type"); |
102 | secret_t secret; | 102 | secret_t secret; |
103 | if(st.empty()) { | 103 | if(st.empty()) { |
104 | secret.from_base64(p.get_param("mac_key")); | 104 | secret.from_base64(p.get_param("mac_key")); |
105 | }else{ | 105 | }else{ |
106 | util::bignum_t s_pub = util::base64_to_bignum(p.get_param("dh_server_public")); | 106 | util::bignum_t s_pub = util::base64_to_bignum(p.get_param("dh_server_public")); |
107 | vector<unsigned char> ck(DH_size(dh)+1); | 107 | vector<unsigned char> ck(DH_size(dh)+1); |
108 | unsigned char *ckptr = &(ck.front())+1; | 108 | unsigned char *ckptr = &(ck.front())+1; |
109 | int cklen = DH_compute_key(ckptr,s_pub,dh); | 109 | int cklen = DH_compute_key(ckptr,s_pub,dh); |
110 | if(cklen<0) | 110 | if(cklen<0) |
111 | throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); | 111 | throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); |
112 | if(cklen && (*ckptr)&0x80) { | 112 | if(cklen && (*ckptr)&0x80) { |
113 | (*(--ckptr)) = 0; ++cklen; | 113 | (*(--ckptr)) = 0; ++cklen; |
114 | } | 114 | } |
115 | unsigned char key_sha1[SHA_DIGEST_LENGTH]; | 115 | unsigned char key_sha1[SHA_DIGEST_LENGTH]; |
116 | SHA1(ckptr,cklen,key_sha1); | 116 | SHA1(ckptr,cklen,key_sha1); |
117 | secret.enxor_from_base64(key_sha1,p.get_param("enc_mac_key")); | 117 | secret.enxor_from_base64(key_sha1,p.get_param("enc_mac_key")); |
118 | } | 118 | } |
119 | int expires_in = 0; | 119 | int expires_in = 0; |
120 | if(p.has_param("expires_in")) { | 120 | if(p.has_param("expires_in")) { |
121 | expires_in = util::string_to_long(p.get_param("expires_in")); | 121 | expires_in = util::string_to_long(p.get_param("expires_in")); |
122 | }else if(p.has_param("issued") && p.has_param("expiry")) { | 122 | }else if(p.has_param("issued") && p.has_param("expiry")) { |
123 | expires_in = util::w3c_to_time(p.get_param("expiry"))-util::w3c_to_time(p.get_param("issued")); | 123 | expires_in = util::w3c_to_time(p.get_param("expiry"))-util::w3c_to_time(p.get_param("issued")); |
124 | }else | 124 | }else |
125 | throw bad_input(OPKELE_CP_ "no expiration information"); | 125 | throw bad_input(OPKELE_CP_ "no expiration information"); |
126 | return store_assoc(server,p.get_param("assoc_handle"),secret,expires_in); | 126 | return store_assoc(server,p.get_param("assoc_handle"),secret,expires_in); |
127 | } | 127 | } |
128 | 128 | ||
129 | string consumer_t::checkid_immediate(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { | 129 | string consumer_t::checkid_immediate(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { |
130 | return checkid_(mode_checkid_immediate,identity,return_to,trust_root,ext); | 130 | return checkid_(mode_checkid_immediate,identity,return_to,trust_root,ext); |
131 | } | 131 | } |
132 | string consumer_t::checkid_setup(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { | 132 | string consumer_t::checkid_setup(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { |
133 | return checkid_(mode_checkid_setup,identity,return_to,trust_root,ext); | 133 | return checkid_(mode_checkid_setup,identity,return_to,trust_root,ext); |
134 | } | 134 | } |
135 | string consumer_t::checkid_(mode_t mode,const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { | 135 | string consumer_t::checkid_(mode_t mode,const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { |
136 | params_t p; | 136 | params_t p; |
137 | if(mode==mode_checkid_immediate) | 137 | if(mode==mode_checkid_immediate) |
138 | p["mode"]="checkid_immediate"; | 138 | p["mode"]="checkid_immediate"; |
139 | else if(mode==mode_checkid_setup) | 139 | else if(mode==mode_checkid_setup) |
140 | p["mode"]="checkid_setup"; | 140 | p["mode"]="checkid_setup"; |
141 | else | 141 | else |
142 | throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); | 142 | throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); |
143 | string iurl = canonicalize(identity); | 143 | string iurl = canonicalize(identity); |
144 | string server, delegate; | 144 | string server, delegate; |
145 | retrieve_links(iurl,server,delegate); | 145 | retrieve_links(iurl,server,delegate); |
146 | p["identity"] = delegate.empty()?iurl:delegate; | 146 | p["identity"] = delegate.empty()?iurl:delegate; |
147 | if(!trust_root.empty()) | 147 | if(!trust_root.empty()) |
148 | p["trust_root"] = trust_root; | 148 | p["trust_root"] = trust_root; |
149 | p["return_to"] = return_to; | 149 | p["return_to"] = return_to; |
150 | try { | 150 | try { |
151 | string ah = find_assoc(server)->handle(); | 151 | string ah = find_assoc(server)->handle(); |
152 | p["assoc_handle"] = ah; | 152 | p["assoc_handle"] = ah; |
153 | }catch(failed_lookup& fl) { | 153 | }catch(failed_lookup& fl) { |
154 | string ah = associate(server)->handle(); | 154 | string ah = associate(server)->handle(); |
155 | p["assoc_handle"] = ah; | 155 | p["assoc_handle"] = ah; |
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); |
159 | } | 159 | } |
160 | 160 | ||
161 | void consumer_t::id_res(const params_t& pin,const string& identity,extension_t *ext) { | 161 | void consumer_t::id_res(const params_t& pin,const string& identity,extension_t *ext) { |
162 | if(pin.has_param("openid.user_setup_url")) | 162 | if(pin.has_param("openid.user_setup_url")) |
163 | throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",pin.get_param("openid.user_setup_url")); | 163 | throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",pin.get_param("openid.user_setup_url")); |
164 | string server,delegate; | 164 | string server,delegate; |
165 | retrieve_links(identity.empty()?pin.get_param("openid.identity"):canonicalize(identity),server,delegate); | 165 | retrieve_links(identity.empty()?pin.get_param("openid.identity"):canonicalize(identity),server,delegate); |
166 | params_t ps; | 166 | params_t ps; |
167 | try { | 167 | try { |
168 | assoc_t assoc = retrieve_assoc(server,pin.get_param("openid.assoc_handle")); | 168 | assoc_t assoc = retrieve_assoc(server,pin.get_param("openid.assoc_handle")); |
169 | if(assoc->is_expired()) | 169 | if(assoc->is_expired()) |
170 | throw id_res_expired_on_delivery(OPKELE_CP_ "retrieve_assoc() has returned expired handle"); | 170 | throw id_res_expired_on_delivery(OPKELE_CP_ "retrieve_assoc() has returned expired handle"); |
171 | const string& sigenc = pin.get_param("openid.sig"); | 171 | const string& sigenc = pin.get_param("openid.sig"); |
172 | vector<unsigned char> sig; | 172 | vector<unsigned char> sig; |
173 | util::decode_base64(sigenc,sig); | 173 | util::decode_base64(sigenc,sig); |
174 | const string& slist = pin.get_param("openid.signed"); | 174 | const string& slist = pin.get_param("openid.signed"); |
175 | string kv; | 175 | string kv; |
176 | string::size_type p = 0; | 176 | string::size_type p = 0; |
177 | while(true) { | 177 | while(true) { |
178 | string::size_type co = slist.find(',',p); | 178 | string::size_type co = slist.find(',',p); |
179 | string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p); | 179 | string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p); |
180 | kv += f; | 180 | kv += f; |
181 | kv += ':'; | 181 | kv += ':'; |
182 | f.insert(0,"openid."); | 182 | f.insert(0,"openid."); |
183 | kv += pin.get_param(f); | 183 | kv += pin.get_param(f); |
184 | kv += '\n'; | 184 | kv += '\n'; |
185 | if(ext) ps[f.substr(sizeof("openid.")-1)] = pin.get_param(f); | 185 | if(ext) ps[f.substr(sizeof("openid.")-1)] = pin.get_param(f); |
186 | if(co==string::npos) | 186 | if(co==string::npos) |
187 | break; | 187 | break; |
188 | p = co+1; | 188 | p = co+1; |
189 | } | 189 | } |
190 | secret_t secret = assoc->secret(); | 190 | secret_t secret = assoc->secret(); |
191 | unsigned int md_len = 0; | 191 | unsigned int md_len = 0; |
192 | unsigned char *md = HMAC( | 192 | unsigned char *md = HMAC( |
193 | EVP_sha1(), | 193 | EVP_sha1(), |
194 | &(secret.front()),secret.size(), | 194 | &(secret.front()),secret.size(), |
195 | (const unsigned char *)kv.data(),kv.length(), | 195 | (const unsigned char *)kv.data(),kv.length(), |
196 | 0,&md_len); | 196 | 0,&md_len); |
197 | if(sig.size()!=md_len || memcmp(&(sig.front()),md,md_len)) | 197 | if(sig.size()!=md_len || memcmp(&(sig.front()),md,md_len)) |
198 | throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); | 198 | throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); |
199 | }catch(failed_lookup& e) { | 199 | }catch(failed_lookup& e) { |
200 | const string& slist = pin.get_param("openid.signed"); | 200 | const string& slist = pin.get_param("openid.signed"); |
201 | string::size_type pp = 0; | 201 | string::size_type pp = 0; |
202 | params_t p; | 202 | params_t p; |
203 | while(true) { | 203 | while(true) { |
204 | string::size_type co = slist.find(',',pp); | 204 | string::size_type co = slist.find(',',pp); |
205 | string f = "openid."; | 205 | string f = "openid."; |
206 | f += (co==string::npos)?slist.substr(pp):slist.substr(pp,co-pp); | 206 | f += (co==string::npos)?slist.substr(pp):slist.substr(pp,co-pp); |
207 | p[f] = pin.get_param(f); | 207 | p[f] = pin.get_param(f); |
208 | if(co==string::npos) | 208 | if(co==string::npos) |
209 | break; | 209 | break; |
210 | pp = co+1; | 210 | pp = co+1; |
211 | } | 211 | } |
212 | p["openid.assoc_handle"] = pin.get_param("openid.assoc_handle"); | 212 | p["openid.assoc_handle"] = pin.get_param("openid.assoc_handle"); |
213 | p["openid.sig"] = pin.get_param("openid.sig"); | 213 | p["openid.sig"] = pin.get_param("openid.sig"); |
214 | p["openid.signed"] = pin.get_param("openid.signed"); | 214 | p["openid.signed"] = pin.get_param("openid.signed"); |
215 | try { | 215 | try { |
216 | string ih = pin.get_param("openid.invalidate_handle"); | 216 | string ih = pin.get_param("openid.invalidate_handle"); |
217 | p["openid.invalidate_handle"] = ih; | 217 | p["openid.invalidate_handle"] = ih; |
218 | }catch(failed_lookup& fl) { } | 218 | }catch(failed_lookup& fl) { } |
219 | try { | 219 | try { |
220 | check_authentication(server,p); | 220 | check_authentication(server,p); |
221 | }catch(failed_check_authentication& fca) { | 221 | }catch(failed_check_authentication& fca) { |
222 | throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); | 222 | throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); |
223 | } | 223 | } |
224 | } | 224 | } |
225 | if(ext) ext->id_res_hook(pin,ps,identity); | 225 | if(ext) ext->id_res_hook(pin,ps); |
226 | } | 226 | } |
227 | 227 | ||
228 | void consumer_t::check_authentication(const string& server,const params_t& p) { | 228 | void consumer_t::check_authentication(const string& server,const params_t& p) { |
229 | string request = "openid.mode=check_authentication"; | 229 | string request = "openid.mode=check_authentication"; |
230 | for(params_t::const_iterator i=p.begin();i!=p.end();++i) { | 230 | for(params_t::const_iterator i=p.begin();i!=p.end();++i) { |
231 | if(i->first!="openid.mode") { | 231 | if(i->first!="openid.mode") { |
232 | request += '&'; | 232 | request += '&'; |
233 | request += i->first; | 233 | request += i->first; |
234 | request += '='; | 234 | request += '='; |
235 | request += util::url_encode(i->second); | 235 | request += util::url_encode(i->second); |
236 | } | 236 | } |
237 | } | 237 | } |
238 | curl_pick_t curl = curl_pick_t::easy_init(); | 238 | curl_pick_t curl = curl_pick_t::easy_init(); |
239 | if(!curl) | 239 | if(!curl) |
240 | throw exception_curl(OPKELE_CP_ "failed to initialize curl"); | 240 | throw exception_curl(OPKELE_CP_ "failed to initialize curl"); |
241 | CURLcode r; | 241 | CURLcode r; |
242 | (r=curl.misc_sets()) | 242 | (r=curl.misc_sets()) |
243 | || (r=curl.easy_setopt(CURLOPT_URL,server.c_str())) | 243 | || (r=curl.easy_setopt(CURLOPT_URL,server.c_str())) |
244 | || (r=curl.easy_setopt(CURLOPT_POST,1)) | 244 | || (r=curl.easy_setopt(CURLOPT_POST,1)) |
245 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) | 245 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) |
246 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) | 246 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) |
247 | || (r=curl.set_write()) | 247 | || (r=curl.set_write()) |
248 | ; | 248 | ; |
249 | if(r) | 249 | if(r) |
250 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); | 250 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); |
251 | if( (r=curl.easy_perform()) ) | 251 | if( (r=curl.easy_perform()) ) |
252 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); | 252 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); |
253 | params_t pp; pp.parse_keyvalues(curl.response); | 253 | params_t pp; pp.parse_keyvalues(curl.response); |
254 | if(pp.has_param("invalidate_handle")) | 254 | if(pp.has_param("invalidate_handle")) |
255 | invalidate_assoc(server,pp.get_param("invalidate_handle")); | 255 | invalidate_assoc(server,pp.get_param("invalidate_handle")); |
256 | if(pp.has_param("is_valid")) { | 256 | if(pp.has_param("is_valid")) { |
257 | if(pp.get_param("is_valid")=="true") | 257 | if(pp.get_param("is_valid")=="true") |
258 | return; | 258 | return; |
259 | }else if(pp.has_param("lifetime")) { | 259 | }else if(pp.has_param("lifetime")) { |
260 | if(util::string_to_long(pp.get_param("lifetime"))) | 260 | if(util::string_to_long(pp.get_param("lifetime"))) |
261 | return; | 261 | return; |
262 | } | 262 | } |
263 | throw failed_check_authentication(OPKELE_CP_ "failed to verify response"); | 263 | throw failed_check_authentication(OPKELE_CP_ "failed to verify response"); |
264 | } | 264 | } |
265 | 265 | ||
266 | void consumer_t::retrieve_links(const string& url,string& server,string& delegate) { | 266 | void consumer_t::retrieve_links(const string& url,string& server,string& delegate) { |
267 | server.erase(); | 267 | server.erase(); |
268 | delegate.erase(); | 268 | delegate.erase(); |
269 | curl_pick_t curl = curl_pick_t::easy_init(); | 269 | curl_pick_t curl = curl_pick_t::easy_init(); |
270 | if(!curl) | 270 | if(!curl) |
271 | throw exception_curl(OPKELE_CP_ "failed to initialize curl"); | 271 | throw exception_curl(OPKELE_CP_ "failed to initialize curl"); |
272 | string& html = curl.response; | 272 | string& html = curl.response; |
273 | CURLcode r; | 273 | CURLcode r; |
274 | (r=curl.misc_sets()) | 274 | (r=curl.misc_sets()) |
275 | || (r=curl.easy_setopt(CURLOPT_URL,url.c_str())) | 275 | || (r=curl.easy_setopt(CURLOPT_URL,url.c_str())) |
276 | || (r=curl.set_write()); | 276 | || (r=curl.set_write()); |
277 | ; | 277 | ; |
278 | if(r) | 278 | if(r) |
279 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); | 279 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); |
280 | r = curl.easy_perform(); | 280 | r = curl.easy_perform(); |
281 | if(r && r!=CURLE_WRITE_ERROR) | 281 | if(r && r!=CURLE_WRITE_ERROR) |
282 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); | 282 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); |
283 | static const char *re_bre = "<\\s*body\\b", *re_hdre = "<\\s*head[^>]*>", | 283 | static const char *re_bre = "<\\s*body\\b", *re_hdre = "<\\s*head[^>]*>", |
284 | *re_lre = "<\\s*link\\b([^>]+)>", | 284 | *re_lre = "<\\s*link\\b([^>]+)>", |
285 | *re_rre = "\\brel\\s*=\\s*['\"]([^'\"]+)['\"]", | 285 | *re_rre = "\\brel\\s*=\\s*['\"]([^'\"]+)['\"]", |
286 | *re_hre = "\\bhref\\s*=\\s*['\"]\\s*([^'\"\\s]+)\\s*['\"]"; | 286 | *re_hre = "\\bhref\\s*=\\s*['\"]\\s*([^'\"\\s]+)\\s*['\"]"; |
287 | pcre_matches_t m1(3), m2(3); | 287 | pcre_matches_t m1(3), m2(3); |
288 | pcre_t bre(re_bre,PCRE_CASELESS); | 288 | pcre_t bre(re_bre,PCRE_CASELESS); |
289 | if(bre.exec(html,m1)>0) | 289 | if(bre.exec(html,m1)>0) |
290 | html.erase(m1.begin(0)); | 290 | html.erase(m1.begin(0)); |
291 | pcre_t hdre(re_hdre,PCRE_CASELESS); | 291 | pcre_t hdre(re_hdre,PCRE_CASELESS); |
292 | if(hdre.exec(html,m1)<=0) | 292 | if(hdre.exec(html,m1)<=0) |
293 | throw bad_input(OPKELE_CP_ "failed to find <head>"); | 293 | throw bad_input(OPKELE_CP_ "failed to find <head>"); |
294 | html.erase(0,m1.end(0)+1); | 294 | html.erase(0,m1.end(0)+1); |
295 | pcre_t lre(re_lre,PCRE_CASELESS), rre(re_rre,PCRE_CASELESS), hre(re_hre,PCRE_CASELESS); | 295 | pcre_t lre(re_lre,PCRE_CASELESS), rre(re_rre,PCRE_CASELESS), hre(re_hre,PCRE_CASELESS); |
296 | bool gotit = false; | 296 | bool gotit = false; |
297 | while( (!gotit) && lre.exec(html,m1)>=2 ) { | 297 | while( (!gotit) && lre.exec(html,m1)>=2 ) { |
298 | static const char *whitespace = " \t"; | 298 | static const char *whitespace = " \t"; |
299 | string attrs(html,m1.begin(1),m1.length(1)); | 299 | string attrs(html,m1.begin(1),m1.length(1)); |
300 | html.erase(0,m1.end(0)+1); | 300 | html.erase(0,m1.end(0)+1); |
301 | if(!( rre.exec(attrs,m1)>=2 && hre.exec(attrs,m2)>=2 )) | 301 | if(!( rre.exec(attrs,m1)>=2 && hre.exec(attrs,m2)>=2 )) |
302 | continue; | 302 | continue; |
303 | string rels(attrs,m1.begin(1),m1.length(1)); | 303 | string rels(attrs,m1.begin(1),m1.length(1)); |
304 | for(string::size_type ns = rels.find_first_not_of(whitespace); | 304 | for(string::size_type ns = rels.find_first_not_of(whitespace); |
305 | ns!=string::npos; | 305 | ns!=string::npos; |
306 | ns=rels.find_first_not_of(whitespace,ns)) { | 306 | ns=rels.find_first_not_of(whitespace,ns)) { |
307 | string::size_type s = rels.find_first_of(whitespace,ns); | 307 | string::size_type s = rels.find_first_of(whitespace,ns); |
308 | string rel; | 308 | string rel; |
309 | if(s==string::npos) { | 309 | if(s==string::npos) { |
310 | rel.assign(rels,ns,string::npos); | 310 | rel.assign(rels,ns,string::npos); |
311 | ns=string::npos; | 311 | ns=string::npos; |
312 | }else{ | 312 | }else{ |
313 | rel.assign(rels,ns,s-ns); | 313 | rel.assign(rels,ns,s-ns); |
314 | ns=s; | 314 | ns=s; |
315 | } | 315 | } |
316 | if(rel=="openid.server") { | 316 | if(rel=="openid.server") { |
317 | server.assign(attrs,m2.begin(1),m2.length(1)); | 317 | server.assign(attrs,m2.begin(1),m2.length(1)); |
318 | if(!delegate.empty()) { | 318 | if(!delegate.empty()) { |
319 | gotit = true; | 319 | gotit = true; |
320 | break; | 320 | break; |
321 | } | 321 | } |
322 | }else if(rel=="openid.delegate") { | 322 | }else if(rel=="openid.delegate") { |
323 | delegate.assign(attrs,m2.begin(1),m2.length(1)); | 323 | delegate.assign(attrs,m2.begin(1),m2.length(1)); |
324 | if(!server.empty()) { | 324 | if(!server.empty()) { |
325 | gotit = true; | 325 | gotit = true; |
326 | break; | 326 | break; |
327 | } | 327 | } |
328 | } | 328 | } |
329 | } | 329 | } |
330 | } | 330 | } |
331 | if(server.empty()) | 331 | if(server.empty()) |
332 | throw failed_assertion(OPKELE_CP_ "The location has no openid.server declaration"); | 332 | throw failed_assertion(OPKELE_CP_ "The location has no openid.server declaration"); |
333 | } | 333 | } |
334 | 334 | ||
335 | assoc_t consumer_t::find_assoc(const string& /* server */) { | 335 | assoc_t consumer_t::find_assoc(const string& /* server */) { |
336 | throw failed_lookup(OPKELE_CP_ "no find_assoc() provided"); | 336 | throw failed_lookup(OPKELE_CP_ "no find_assoc() provided"); |
337 | } | 337 | } |
338 | 338 | ||
339 | string consumer_t::normalize(const string& url) { | 339 | string consumer_t::normalize(const string& url) { |
340 | string rv = url; | 340 | string rv = url; |
341 | // strip leading and trailing spaces | 341 | // strip leading and trailing spaces |
342 | string::size_type i = rv.find_first_not_of(" \t\r\n"); | 342 | string::size_type i = rv.find_first_not_of(" \t\r\n"); |
343 | if(i==string::npos) | 343 | if(i==string::npos) |
344 | throw bad_input(OPKELE_CP_ "empty URL"); | 344 | throw bad_input(OPKELE_CP_ "empty URL"); |
345 | if(i) | 345 | if(i) |
346 | rv.erase(0,i); | 346 | rv.erase(0,i); |
347 | i = rv.find_last_not_of(" \t\r\n"); | 347 | i = rv.find_last_not_of(" \t\r\n"); |
348 | assert(i!=string::npos); | 348 | assert(i!=string::npos); |
349 | if(i<(rv.length()-1)) | 349 | if(i<(rv.length()-1)) |
350 | rv.erase(i+1); | 350 | rv.erase(i+1); |
351 | // add missing http:// | 351 | // add missing http:// |
352 | i = rv.find("://"); | 352 | i = rv.find("://"); |
353 | if(i==string::npos) { // primitive. but do we need more? | 353 | if(i==string::npos) { // primitive. but do we need more? |
354 | rv.insert(0,"http://"); | 354 | rv.insert(0,"http://"); |
355 | i = sizeof("http://")-1; | 355 | i = sizeof("http://")-1; |
356 | }else{ | 356 | }else{ |
357 | i += sizeof("://")-1; | 357 | i += sizeof("://")-1; |
358 | } | 358 | } |
359 | string::size_type qm = rv.find('?',i); | 359 | string::size_type qm = rv.find('?',i); |
360 | string::size_type sl = rv.find('/',i); | 360 | string::size_type sl = rv.find('/',i); |
361 | if(qm!=string::npos) { | 361 | if(qm!=string::npos) { |
362 | if(sl==string::npos || sl>qm) | 362 | if(sl==string::npos || sl>qm) |
363 | rv.insert(qm,1,'/'); | 363 | rv.insert(qm,1,'/'); |
364 | }else{ | 364 | }else{ |
365 | if(sl==string::npos) | 365 | if(sl==string::npos) |
366 | rv += '/'; | 366 | rv += '/'; |
367 | } | 367 | } |
368 | return rv; | 368 | return rv; |
369 | } | 369 | } |
370 | 370 | ||
371 | string consumer_t::canonicalize(const string& url) { | 371 | string consumer_t::canonicalize(const string& url) { |
372 | string rv = normalize(url); | 372 | string rv = normalize(url); |
373 | curl_t curl = curl_t::easy_init(); | 373 | curl_t curl = curl_t::easy_init(); |
374 | if(!curl) | 374 | if(!curl) |
375 | throw exception_curl(OPKELE_CP_ "failed to initialize curl()"); | 375 | throw exception_curl(OPKELE_CP_ "failed to initialize curl()"); |
376 | string html; | 376 | string html; |
377 | CURLcode r; | 377 | CURLcode r; |
378 | (r=curl.misc_sets()) | 378 | (r=curl.misc_sets()) |
379 | || (r=curl.easy_setopt(CURLOPT_URL,rv.c_str())) | 379 | || (r=curl.easy_setopt(CURLOPT_URL,rv.c_str())) |
380 | || (r=curl.easy_setopt(CURLOPT_NOBODY,1)) | 380 | || (r=curl.easy_setopt(CURLOPT_NOBODY,1)) |
381 | ; | 381 | ; |
382 | if(r) | 382 | if(r) |
383 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); | 383 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); |
384 | r = curl.easy_perform(); | 384 | r = curl.easy_perform(); |
385 | if(r) | 385 | if(r) |
386 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); | 386 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); |
387 | const char *eu = 0; | 387 | const char *eu = 0; |
388 | r = curl.easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu); | 388 | r = curl.easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu); |
389 | if(r) | 389 | if(r) |
390 | throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r); | 390 | throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r); |
391 | rv = eu; | 391 | rv = eu; |
392 | return normalize(rv); | 392 | return normalize(rv); |
393 | } | 393 | } |
394 | 394 | ||
395 | } | 395 | } |
diff --git a/lib/discovery.cc b/lib/discovery.cc index d868308..93409f4 100644 --- a/lib/discovery.cc +++ b/lib/discovery.cc | |||
@@ -1,446 +1,537 @@ | |||
1 | #include <list> | 1 | #include <list> |
2 | #include <opkele/curl.h> | 2 | #include <opkele/curl.h> |
3 | #include <opkele/expat.h> | 3 | #include <opkele/expat.h> |
4 | #include <opkele/uris.h> | 4 | #include <opkele/uris.h> |
5 | #include <opkele/discovery.h> | 5 | #include <opkele/discovery.h> |
6 | #include <opkele/exception.h> | 6 | #include <opkele/exception.h> |
7 | #include <opkele/util.h> | 7 | #include <opkele/util.h> |
8 | #include <opkele/tidy.h> | 8 | #include <opkele/tidy.h> |
9 | #include <opkele/debug.h> | 9 | #include <opkele/debug.h> |
10 | 10 | ||
11 | #include "config.h" | 11 | #include "config.h" |
12 | 12 | ||
13 | #define XRDS_HEADER "X-XRDS-Location" | 13 | #define XRDS_HEADER "X-XRDS-Location" |
14 | #define CT_HEADER "Content-Type" | 14 | #define CT_HEADER "Content-Type" |
15 | 15 | ||
16 | namespace opkele { | 16 | namespace opkele { |
17 | using std::list; | 17 | using std::list; |
18 | using xrd::XRD_t; | 18 | using xrd::XRD_t; |
19 | using xrd::service_t; | 19 | using xrd::service_t; |
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"; |
22 | static const char *i_leaders = "=@+$!("; | 25 | static const char *i_leaders = "=@+$!("; |
23 | static const size_t max_html = 16384; | 26 | static const size_t max_html = 16384; |
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) { |
26 | return !strcasecmp(n,qen); | 43 | return !strcasecmp(n,qen); |
27 | } | 44 | } |
28 | static inline bool is_element(const XML_Char *n,const char *en) { | 45 | static inline bool is_element(const XML_Char *n,const char *en) { |
29 | if(!strcasecmp(n,en)) return true; | 46 | if(!strcasecmp(n,en)) return true; |
30 | int nl = strlen(n), enl = strlen(en); | 47 | int nl = strlen(n), enl = strlen(en); |
31 | if( (nl>=(enl+1)) && n[nl-enl-1]=='\t' | 48 | if( (nl>=(enl+1)) && n[nl-enl-1]=='\t' |
32 | && !strcasecmp(&n[nl-enl],en) ) | 49 | && !strcasecmp(&n[nl-enl],en) ) |
33 | return true; | 50 | return true; |
34 | return false; | 51 | return false; |
35 | } | 52 | } |
36 | 53 | ||
37 | static long element_priority(const XML_Char **a) { | 54 | static long element_priority(const XML_Char **a) { |
38 | for(;*a;++a) | 55 | for(;*a;++a) |
39 | if(!strcasecmp(*(a++),"priority")) { | 56 | if(!strcasecmp(*(a++),"priority")) { |
40 | long rv; | 57 | long rv; |
41 | return (sscanf(*a,"%ld",&rv)==1)?rv:-1; | 58 | return (sscanf(*a,"%ld",&rv)==1)?rv:-1; |
42 | } | 59 | } |
43 | return -1; | 60 | return -1; |
44 | } | 61 | } |
45 | 62 | ||
46 | class idigger_t : public util::curl_t, public util::expat_t { | 63 | class idigger_t : public util::curl_t, public util::expat_t { |
47 | public: | 64 | public: |
48 | string xri_proxy; | 65 | string xri_proxy; |
49 | 66 | ||
50 | enum { | 67 | enum { |
51 | xmode_html = 1, xmode_xrd = 2 | 68 | xmode_html = 1, xmode_xrd = 2, xmode_cid = 4 |
52 | }; | 69 | }; |
53 | int xmode; | 70 | int xmode; |
54 | 71 | ||
55 | string xrds_location; | 72 | string xrds_location; |
56 | string http_content_type; | 73 | string http_content_type; |
57 | service_t html_openid1; | 74 | service_t html_openid1; |
58 | service_t html_openid2; | 75 | service_t html_openid2; |
59 | string cdata_buf; | 76 | string cdata_buf; |
60 | long status_code; | 77 | long status_code; |
61 | string status_string; | 78 | string status_string; |
62 | 79 | ||
63 | typedef list<string> pt_stack_t; | 80 | typedef list<string> pt_stack_t; |
64 | pt_stack_t pt_stack; | 81 | pt_stack_t pt_stack; |
65 | int skipping; | 82 | int skipping; |
66 | bool parser_choked; | 83 | bool parser_choked; |
67 | string save_html; | 84 | string save_html; |
68 | 85 | ||
69 | XRD_t *xrd; | 86 | XRD_t *xrd; |
70 | service_t *xrd_service; | 87 | service_t *xrd_service; |
71 | string* cdata; | 88 | string* cdata; |
72 | 89 | ||
73 | idigger_t() | 90 | idigger_t() |
74 | : util::curl_t(easy_init()), | 91 | : util::curl_t(easy_init()), |
75 | util::expat_t(0), | 92 | util::expat_t(0), |
76 | xri_proxy(XRI_PROXY_URL) { | 93 | xri_proxy(XRI_PROXY_URL) { |
77 | CURLcode r; | 94 | CURLcode r; |
78 | (r=misc_sets()) | 95 | (r=misc_sets()) |
79 | || (r=set_write()) | 96 | || (r=set_write()) |
80 | || (r=set_header()) | 97 | || (r=set_header()) |
81 | ; | 98 | ; |
82 | if(r) | 99 | if(r) |
83 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); | 100 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); |
84 | } | 101 | } |
85 | ~idigger_t() throw() { } | 102 | ~idigger_t() throw() { } |
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); |
93 | assert(lsc!=string::npos); | 111 | assert(lsc!=string::npos); |
94 | if(!strncasecmp(identity.c_str()+fsc,"xri://",sizeof("xri://")-1)) | 112 | if(!strncasecmp(identity.c_str()+fsc,"xri://",sizeof("xri://")-1)) |
95 | fsc += sizeof("xri://")-1; | 113 | fsc += sizeof("xri://")-1; |
96 | if((fsc+1)>=lsc) | 114 | if((fsc+1)>=lsc) |
97 | throw bad_input(OPKELE_CP_ "not a character of importance in identity"); | 115 | throw bad_input(OPKELE_CP_ "not a character of importance in identity"); |
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) |
116 | id.insert(0,"http://"); | 163 | id.insert(0,"http://"); |
117 | string::size_type fp = id.find('#'); | 164 | string::size_type fp = id.find('#'); |
118 | if(fp!=string::npos) { | 165 | if(fp!=string::npos) { |
119 | string::size_type qp = id.find('?'); | 166 | string::size_type qp = id.find('?'); |
120 | if(qp==string::npos || qp<fp) | 167 | if(qp==string::npos || qp<fp) |
121 | id.erase(fp); | 168 | id.erase(fp); |
122 | else if(qp>fp) | 169 | else if(qp>fp) |
123 | id.erase(fp,qp-fp); | 170 | id.erase(fp,qp-fp); |
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; |
128 | CURLcode r = easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu); | 175 | CURLcode r = easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu); |
129 | if(r) | 176 | if(r) |
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()); |
144 | if(r) | 200 | if(r) |
145 | throw exception_curl(OPKELE_CP_ "failed to set culry urlie",r); | 201 | throw exception_curl(OPKELE_CP_ "failed to set culry urlie",r); |
146 | 202 | ||
147 | http_content_type.clear(); | 203 | http_content_type.clear(); |
148 | xmode = xm; | 204 | xmode = xm; |
149 | prepare_to_parse(); | 205 | prepare_to_parse(); |
150 | if(xmode&xmode_html) { | 206 | if(xmode&xmode_html) { |
151 | xrds_location.clear(); | 207 | xrds_location.clear(); |
152 | save_html.clear(); | 208 | save_html.clear(); |
153 | save_html.reserve(max_html); | 209 | save_html.reserve(max_html); |
154 | } | 210 | } |
155 | xrd = &result.xrd; | 211 | xrd = &idis.xrd; |
156 | 212 | ||
157 | r = easy_perform(); | 213 | r = easy_perform(); |
158 | if(r && r!=CURLE_WRITE_ERROR) | 214 | if(r && r!=CURLE_WRITE_ERROR) |
159 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); | 215 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); |
160 | 216 | ||
161 | if(!parser_choked) { | 217 | if(!parser_choked) { |
162 | parse(0,0,true); | 218 | parse(0,0,true); |
163 | }else{ | 219 | }else{ |
164 | /* TODO: do not bother if we've seen xml */ | 220 | /* TODO: do not bother if we've seen xml */ |
165 | try { | 221 | try { |
166 | util::tidy_doc_t td = util::tidy_doc_t::create(); | 222 | util::tidy_doc_t td = util::tidy_doc_t::create(); |
167 | if(!td) | 223 | if(!td) |
168 | throw exception_tidy(OPKELE_CP_ "failed to create htmltidy document"); | 224 | throw exception_tidy(OPKELE_CP_ "failed to create htmltidy document"); |
169 | #ifndef NDEBUG | 225 | #ifndef NDEBUG |
170 | td.opt_set(TidyQuiet,false); | 226 | td.opt_set(TidyQuiet,false); |
171 | td.opt_set(TidyShowWarnings,false); | 227 | td.opt_set(TidyShowWarnings,false); |
172 | #endif /* NDEBUG */ | 228 | #endif /* NDEBUG */ |
173 | td.opt_set(TidyForceOutput,true); | 229 | td.opt_set(TidyForceOutput,true); |
174 | td.opt_set(TidyXhtmlOut,true); | 230 | td.opt_set(TidyXhtmlOut,true); |
175 | td.opt_set(TidyDoctypeMode,TidyDoctypeOmit); | 231 | td.opt_set(TidyDoctypeMode,TidyDoctypeOmit); |
176 | td.opt_set(TidyMark,false); | 232 | td.opt_set(TidyMark,false); |
177 | if(td.parse_string(save_html)<=0) | 233 | if(td.parse_string(save_html)<=0) |
178 | throw exception_tidy(OPKELE_CP_ "tidy failed to parse document"); | 234 | throw exception_tidy(OPKELE_CP_ "tidy failed to parse document"); |
179 | if(td.clean_and_repair()<=0) | 235 | if(td.clean_and_repair()<=0) |
180 | throw exception_tidy(OPKELE_CP_ "tidy failed to clean and repair"); | 236 | throw exception_tidy(OPKELE_CP_ "tidy failed to clean and repair"); |
181 | util::tidy_buf_t tide; | 237 | util::tidy_buf_t tide; |
182 | if(td.save_buffer(tide)<=0) | 238 | if(td.save_buffer(tide)<=0) |
183 | throw exception_tidy(OPKELE_CP_ "tidy failed to save buffer"); | 239 | throw exception_tidy(OPKELE_CP_ "tidy failed to save buffer"); |
184 | prepare_to_parse(); | 240 | prepare_to_parse(); |
185 | parse(tide.c_str(),tide.size(),true); | 241 | parse(tide.c_str(),tide.size(),true); |
186 | }catch(exception_tidy& et) { } | 242 | }catch(exception_tidy& et) { } |
187 | } | 243 | } |
188 | save_html.clear(); | 244 | save_html.clear(); |
189 | } | 245 | } |
190 | 246 | ||
191 | void prepare_to_parse() { | 247 | void prepare_to_parse() { |
192 | (*(expat_t*)this) = parser_create_ns(); | 248 | (*(expat_t*)this) = parser_create_ns(); |
193 | set_user_data(); set_element_handler(); | 249 | set_user_data(); set_element_handler(); |
194 | set_character_data_handler(); | 250 | set_character_data_handler(); |
195 | 251 | ||
196 | if(xmode&xmode_html) { | 252 | if(xmode&xmode_html) { |
197 | html_openid1.clear(); html_openid2.clear(); | 253 | html_openid1.clear(); html_openid2.clear(); |
198 | parser_choked = false; | 254 | parser_choked = false; |
199 | } | 255 | } |
200 | 256 | ||
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(); |
203 | } | 260 | } |
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()) { |
211 | html_openid2.types.insert(STURI_OPENID20); | 265 | html_openid2.types.insert(STURI_OPENID20); |
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 | } |
214 | } | 274 | } |
215 | 275 | ||
216 | size_t write(void *p,size_t s,size_t nm) { | 276 | size_t write(void *p,size_t s,size_t nm) { |
217 | /* TODO: limit total size */ | 277 | /* TODO: limit total size */ |
218 | size_t bytes = s*nm; | 278 | size_t bytes = s*nm; |
219 | const char *inbuf = (const char*)p; | 279 | const char *inbuf = (const char*)p; |
220 | if(xmode&xmode_html) { | 280 | if(xmode&xmode_html) { |
221 | size_t mbts = save_html.capacity()-save_html.size(); | 281 | size_t mbts = save_html.capacity()-save_html.size(); |
222 | size_t bts = 0; | 282 | size_t bts = 0; |
223 | if(mbts>0) { | 283 | if(mbts>0) { |
224 | bts = (bytes>mbts)?mbts:bytes; | 284 | bts = (bytes>mbts)?mbts:bytes; |
225 | save_html.append(inbuf,bts); | 285 | save_html.append(inbuf,bts); |
226 | } | 286 | } |
227 | if(skipping<0) return bts; | 287 | if(skipping<0) return bts; |
228 | } | 288 | } |
229 | if(skipping<0) return 0; | 289 | if(skipping<0) return 0; |
230 | bool rp = parse(inbuf,bytes,false); | 290 | bool rp = parse(inbuf,bytes,false); |
231 | if(!rp) { | 291 | if(!rp) { |
232 | parser_choked = true; | 292 | parser_choked = true; |
233 | skipping = -1; | 293 | skipping = -1; |
234 | if(!(xmode&xmode_html)) | 294 | if(!(xmode&xmode_html)) |
235 | bytes = 0; | 295 | bytes = 0; |
236 | } | 296 | } |
237 | return bytes; | 297 | return bytes; |
238 | } | 298 | } |
239 | size_t header(void *p,size_t s,size_t nm) { | 299 | size_t header(void *p,size_t s,size_t nm) { |
240 | size_t bytes = s*nm; | 300 | size_t bytes = s*nm; |
241 | const char *h = (const char*)p; | 301 | const char *h = (const char*)p; |
242 | const char *colon = (const char*)memchr(p,':',bytes); | 302 | const char *colon = (const char*)memchr(p,':',bytes); |
243 | const char *space = (const char*)memchr(p,' ',bytes); | 303 | const char *space = (const char*)memchr(p,' ',bytes); |
244 | if(space && ( (!colon) || space<colon ) ) { | 304 | if(space && ( (!colon) || space<colon ) ) { |
245 | xrds_location.clear(); http_content_type.clear(); | 305 | xrds_location.clear(); http_content_type.clear(); |
246 | }else if(colon) { | 306 | }else if(colon) { |
247 | const char *hv = ++colon; | 307 | const char *hv = ++colon; |
248 | int hnl = colon-h; | 308 | int hnl = colon-h; |
249 | int rb; | 309 | int rb; |
250 | for(rb = bytes-hnl-1;rb>0 && isspace(*hv);++hv,--rb); | 310 | for(rb = bytes-hnl-1;rb>0 && isspace(*hv);++hv,--rb); |
251 | while(rb>0 && isspace(hv[rb-1])) --rb; | 311 | while(rb>0 && isspace(hv[rb-1])) --rb; |
252 | if(rb) { | 312 | if(rb) { |
253 | if( (hnl>=sizeof(XRDS_HEADER)) | 313 | if( (hnl>=sizeof(XRDS_HEADER)) |
254 | && !strncasecmp(h,XRDS_HEADER":", | 314 | && !strncasecmp(h,XRDS_HEADER":", |
255 | sizeof(XRDS_HEADER)) ) { | 315 | sizeof(XRDS_HEADER)) ) { |
256 | xrds_location.assign(hv,rb); | 316 | xrds_location.assign(hv,rb); |
257 | }else if( (hnl>=sizeof(CT_HEADER)) | 317 | }else if( (hnl>=sizeof(CT_HEADER)) |
258 | && !strncasecmp(h,CT_HEADER":", | 318 | && !strncasecmp(h,CT_HEADER":", |
259 | sizeof(CT_HEADER)) ) { | 319 | sizeof(CT_HEADER)) ) { |
260 | const char *sc = (const char*)memchr( | 320 | const char *sc = (const char*)memchr( |
261 | hv,';',rb); | 321 | hv,';',rb); |
262 | http_content_type.assign(hv,sc?(sc-hv):rb); | 322 | http_content_type.assign(hv,sc?(sc-hv):rb); |
263 | } | 323 | } |
264 | } | 324 | } |
265 | } | 325 | } |
266 | return curl_t::header(p,s,nm); | 326 | return curl_t::header(p,s,nm); |
267 | } | 327 | } |
268 | 328 | ||
269 | void start_element(const XML_Char *n,const XML_Char **a) { | 329 | void start_element(const XML_Char *n,const XML_Char **a) { |
270 | if(skipping<0) return; | 330 | if(skipping<0) return; |
271 | if(skipping) { | 331 | if(skipping) { |
272 | if(xmode&xmode_html) | 332 | if(xmode&xmode_html) |
273 | html_start_element(n,a); | 333 | html_start_element(n,a); |
274 | ++skipping; return; | 334 | ++skipping; return; |
275 | } | 335 | } |
276 | if(pt_stack.empty()) { | 336 | if(pt_stack.empty()) { |
277 | if(is_qelement(n,NSURI_XRDS "\tXRDS")) | 337 | if(is_qelement(n,NSURI_XRDS "\tXRDS")) |
278 | return; | 338 | return; |
279 | if(is_qelement(n,NSURI_XRD "\tXRD")) { | 339 | if(is_qelement(n,NSURI_XRD "\tXRD")) { |
280 | assert(xrd); | 340 | assert(xrd); |
281 | xrd->clear(); | 341 | xrd->clear(); |
282 | pt_stack.push_back(n); | 342 | pt_stack.push_back(n); |
283 | }else if(xmode&xmode_html) { | 343 | }else if(xmode&xmode_html) { |
284 | html_start_element(n,a); | 344 | html_start_element(n,a); |
285 | }else{ | 345 | }else{ |
286 | skipping = -1; | 346 | skipping = -1; |
287 | } | 347 | } |
288 | }else{ | 348 | }else{ |
289 | int pt_s = pt_stack.size(); | 349 | int pt_s = pt_stack.size(); |
290 | if(pt_s==1) { | 350 | if(pt_s==1) { |
291 | if(is_qelement(n,NSURI_XRD "\tCanonicalID")) { | 351 | if(is_qelement(n,NSURI_XRD "\tCanonicalID")) { |
292 | assert(xrd); | 352 | assert(xrd); |
293 | cdata = &(xrd->canonical_ids.add(element_priority(a),string())); | 353 | cdata = &(xrd->canonical_ids.add(element_priority(a),string())); |
294 | }else if(is_qelement(n,NSURI_XRD "\tLocalID")) { | 354 | }else if(is_qelement(n,NSURI_XRD "\tLocalID")) { |
295 | assert(xrd); | 355 | assert(xrd); |
296 | cdata = &(xrd->local_ids.add(element_priority(a),string())); | 356 | cdata = &(xrd->local_ids.add(element_priority(a),string())); |
297 | }else if(is_qelement(n,NSURI_XRD "\tProviderID")) { | 357 | }else if(is_qelement(n,NSURI_XRD "\tProviderID")) { |
298 | assert(xrd); | 358 | assert(xrd); |
299 | cdata = &(xrd->provider_id); | 359 | cdata = &(xrd->provider_id); |
300 | }else if(is_qelement(n,NSURI_XRD "\tService")) { | 360 | }else if(is_qelement(n,NSURI_XRD "\tService")) { |
301 | assert(xrd); | 361 | assert(xrd); |
302 | xrd_service = &(xrd->services.add(element_priority(a), | 362 | xrd_service = &(xrd->services.add(element_priority(a), |
303 | service_t())); | 363 | service_t())); |
304 | pt_stack.push_back(n); | 364 | pt_stack.push_back(n); |
305 | }else if(is_qelement(n,NSURI_XRD "\tStatus")) { | 365 | }else if(is_qelement(n,NSURI_XRD "\tStatus")) { |
306 | for(;*a;) { | 366 | for(;*a;) { |
307 | if(!strcasecmp(*(a++),"code")) { | 367 | if(!strcasecmp(*(a++),"code")) { |
308 | if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) { | 368 | if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) { |
309 | cdata = &status_string; | 369 | cdata = &status_string; |
310 | pt_stack.push_back(n); | 370 | pt_stack.push_back(n); |
311 | break; | 371 | break; |
312 | } | 372 | } |
313 | } | 373 | }else |
374 | ++a; | ||
314 | } | 375 | } |
315 | }else if(is_qelement(n,NSURI_XRD "\tExpires")) { | 376 | }else if(is_qelement(n,NSURI_XRD "\tExpires")) { |
316 | assert(xrd); | 377 | assert(xrd); |
317 | cdata_buf.clear(); | 378 | cdata_buf.clear(); |
318 | cdata = &cdata_buf; | 379 | cdata = &cdata_buf; |
319 | }else if(xmode&xmode_html) { | 380 | }else if(xmode&xmode_html) { |
320 | html_start_element(n,a); | 381 | html_start_element(n,a); |
321 | }else{ | 382 | }else{ |
322 | skipping = 1; | 383 | skipping = 1; |
323 | } | 384 | } |
324 | }else if(pt_s==2) { | 385 | }else if(pt_s==2) { |
325 | if(is_qelement(pt_stack.back().c_str(), NSURI_XRD "\tService")) { | 386 | if(is_qelement(pt_stack.back().c_str(), NSURI_XRD "\tService")) { |
326 | if(is_qelement(n,NSURI_XRD "\tType")) { | 387 | if(is_qelement(n,NSURI_XRD "\tType")) { |
327 | assert(xrd); assert(xrd_service); | 388 | assert(xrd); assert(xrd_service); |
328 | cdata_buf.clear(); | 389 | cdata_buf.clear(); |
329 | cdata = &cdata_buf; | 390 | cdata = &cdata_buf; |
330 | }else if(is_qelement(n,NSURI_XRD "\tURI")) { | 391 | }else if(is_qelement(n,NSURI_XRD "\tURI")) { |
331 | assert(xrd); assert(xrd_service); | 392 | assert(xrd); assert(xrd_service); |
332 | cdata = &(xrd_service->uris.add(element_priority(a),string())); | 393 | cdata = &(xrd_service->uris.add(element_priority(a),string())); |
333 | }else if(is_qelement(n,NSURI_XRD "\tLocalID") | 394 | }else if(is_qelement(n,NSURI_XRD "\tLocalID") |
334 | || is_qelement(n,NSURI_OPENID10 "\tDelegate") ) { | 395 | || is_qelement(n,NSURI_OPENID10 "\tDelegate") ) { |
335 | assert(xrd); assert(xrd_service); | 396 | assert(xrd); assert(xrd_service); |
336 | cdata = &(xrd_service->local_ids.add(element_priority(a),string())); | 397 | cdata = &(xrd_service->local_ids.add(element_priority(a),string())); |
337 | }else if(is_qelement(n,NSURI_XRD "\tProviderID")) { | 398 | }else if(is_qelement(n,NSURI_XRD "\tProviderID")) { |
338 | assert(xrd); assert(xrd_service); | 399 | assert(xrd); assert(xrd_service); |
339 | cdata = &(xrd_service->provider_id); | 400 | cdata = &(xrd_service->provider_id); |
340 | }else{ | 401 | }else{ |
341 | skipping = 1; | 402 | skipping = 1; |
342 | } | 403 | } |
343 | }else | 404 | }else |
344 | skipping = 1; | 405 | skipping = 1; |
345 | }else if(xmode&xmode_html) { | 406 | }else if(xmode&xmode_html) { |
346 | html_start_element(n,a); | 407 | html_start_element(n,a); |
347 | }else{ | 408 | }else{ |
348 | skipping = 1; | 409 | skipping = 1; |
349 | } | 410 | } |
350 | } | 411 | } |
351 | } | 412 | } |
352 | void end_element(const XML_Char *n) { | 413 | void end_element(const XML_Char *n) { |
353 | if(skipping<0) return; | 414 | if(skipping<0) return; |
354 | if(skipping) { | 415 | if(skipping) { |
355 | --skipping; return; | 416 | --skipping; return; |
356 | } | 417 | } |
357 | if(is_qelement(n,NSURI_XRD "\tType")) { | 418 | if(is_qelement(n,NSURI_XRD "\tType")) { |
358 | assert(xrd); assert(xrd_service); assert(cdata==&cdata_buf); | 419 | assert(xrd); assert(xrd_service); assert(cdata==&cdata_buf); |
359 | xrd_service->types.insert(cdata_buf); | 420 | xrd_service->types.insert(cdata_buf); |
360 | }else if(is_qelement(n,NSURI_XRD "\tService")) { | 421 | }else if(is_qelement(n,NSURI_XRD "\tService")) { |
361 | assert(xrd); assert(xrd_service); | 422 | assert(xrd); assert(xrd_service); |
362 | assert(!pt_stack.empty()); | 423 | assert(!pt_stack.empty()); |
363 | assert(pt_stack.back()==(NSURI_XRD "\tService")); | 424 | assert(pt_stack.back()==(NSURI_XRD "\tService")); |
364 | pt_stack.pop_back(); | 425 | pt_stack.pop_back(); |
365 | xrd_service = 0; | 426 | xrd_service = 0; |
366 | }else if(is_qelement(n,NSURI_XRD "\tStatus")) { | 427 | }else if(is_qelement(n,NSURI_XRD "\tStatus")) { |
367 | assert(xrd); | 428 | assert(xrd); |
368 | if(is_qelement(pt_stack.back().c_str(),n)) { | 429 | if(is_qelement(pt_stack.back().c_str(),n)) { |
369 | assert(cdata==&status_string); | 430 | assert(cdata==&status_string); |
370 | pt_stack.pop_back(); | 431 | pt_stack.pop_back(); |
371 | if(status_code!=100) | 432 | if(status_code!=100) |
372 | skipping = -1; | 433 | skipping = -1; |
373 | } | 434 | } |
374 | }else if(is_qelement(n,NSURI_XRD "\tExpires")) { | 435 | }else if(is_qelement(n,NSURI_XRD "\tExpires")) { |
375 | assert(xrd); | 436 | assert(xrd); |
376 | xrd->expires = util::w3c_to_time(cdata_buf); | 437 | xrd->expires = util::w3c_to_time(cdata_buf); |
377 | }else if((xmode&xmode_html) && is_element(n,"head")) { | 438 | }else if((xmode&xmode_html) && is_element(n,"head")) { |
378 | skipping = -1; | 439 | skipping = -1; |
379 | } | 440 | } |
380 | cdata = 0; | 441 | cdata = 0; |
381 | } | 442 | } |
382 | void character_data(const XML_Char *s,int l) { | 443 | void character_data(const XML_Char *s,int l) { |
383 | if(skipping) return; | 444 | if(skipping) return; |
384 | if(cdata) cdata->append(s,l); | 445 | if(cdata) cdata->append(s,l); |
385 | } | 446 | } |
386 | 447 | ||
387 | void html_start_element(const XML_Char *n,const XML_Char **a) { | 448 | void html_start_element(const XML_Char *n,const XML_Char **a) { |
388 | if(is_element(n,"meta")) { | 449 | if(is_element(n,"meta")) { |
389 | bool heq = false; | 450 | bool heq = false; |
390 | string l; | 451 | string l; |
391 | for(;*a;a+=2) { | 452 | for(;*a;a+=2) { |
392 | if(!( strcasecmp(a[0],"http-equiv") | 453 | if(!( strcasecmp(a[0],"http-equiv") |
393 | || strcasecmp(a[1],XRDS_HEADER) )) | 454 | || strcasecmp(a[1],XRDS_HEADER) )) |
394 | heq = true; | 455 | heq = true; |
395 | else if(!strcasecmp(a[0],"content")) | 456 | else if(!strcasecmp(a[0],"content")) |
396 | l.assign(a[1]); | 457 | l.assign(a[1]); |
397 | } | 458 | } |
398 | if(heq) | 459 | if(heq) |
399 | xrds_location = l; | 460 | xrds_location = l; |
400 | }else if(is_element(n,"link")) { | 461 | }else if(is_element(n,"link")) { |
401 | string rels; | 462 | string rels; |
402 | string href; | 463 | string href; |
403 | for(;*a;a+=2) { | 464 | for(;*a;a+=2) { |
404 | if( !strcasecmp(a[0],"rel") ) { | 465 | if( !strcasecmp(a[0],"rel") ) { |
405 | rels.assign(a[1]); | 466 | rels.assign(a[1]); |
406 | }else if( !strcasecmp(a[0],"href") ) { | 467 | }else if( !strcasecmp(a[0],"href") ) { |
407 | const char *ns = a[1]; | 468 | const char *ns = a[1]; |
408 | for(;*ns && isspace(*ns);++ns); | 469 | for(;*ns && isspace(*ns);++ns); |
409 | href.assign(ns); | 470 | href.assign(ns); |
410 | string::size_type lns=href.find_last_not_of(whitespace); | 471 | string::size_type lns=href.find_last_not_of(whitespace); |
411 | href.erase(lns+1); | 472 | href.erase(lns+1); |
412 | } | 473 | } |
413 | } | 474 | } |
414 | for(string::size_type ns=rels.find_first_not_of(whitespace); | 475 | for(string::size_type ns=rels.find_first_not_of(whitespace); |
415 | ns!=string::npos; ns=rels.find_first_not_of(whitespace,ns)) { | 476 | ns!=string::npos; ns=rels.find_first_not_of(whitespace,ns)) { |
416 | string::size_type s = rels.find_first_of(whitespace,ns); | 477 | string::size_type s = rels.find_first_of(whitespace,ns); |
417 | string rel; | 478 | string rel; |
418 | if(s==string::npos) { | 479 | if(s==string::npos) { |
419 | rel.assign(rels,ns,string::npos); | 480 | rel.assign(rels,ns,string::npos); |
420 | ns = string::npos; | 481 | ns = string::npos; |
421 | }else{ | 482 | }else{ |
422 | rel.assign(rels,ns,s-ns); | 483 | rel.assign(rels,ns,s-ns); |
423 | ns = s; | 484 | ns = s; |
424 | } | 485 | } |
425 | if(rel=="openid.server") | 486 | if(rel=="openid.server") |
426 | html_openid1.uris.add(-1,href); | 487 | html_openid1.uris.add(-1,href); |
427 | else if(rel=="openid.delegate") | 488 | else if(rel=="openid.delegate") |
428 | html_openid1.local_ids.add(-1,href); | 489 | html_openid1.local_ids.add(-1,href); |
429 | else if(rel=="openid2.provider") | 490 | else if(rel=="openid2.provider") |
430 | html_openid2.uris.add(-1,href); | 491 | html_openid2.uris.add(-1,href); |
431 | else if(rel=="openid2.local_id") | 492 | else if(rel=="openid2.local_id") |
432 | html_openid2.local_ids.add(-1,href); | 493 | html_openid2.local_ids.add(-1,href); |
433 | } | 494 | } |
434 | }else if(is_element(n,"body")) { | 495 | }else if(is_element(n,"body")) { |
435 | skipping = -1; | 496 | skipping = -1; |
436 | } | 497 | } |
437 | } | 498 | } |
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 | } |
445 | 536 | ||
446 | } | 537 | } |
diff --git a/lib/extension.cc b/lib/extension.cc index 8f22562..6451249 100644 --- a/lib/extension.cc +++ b/lib/extension.cc | |||
@@ -1,15 +1,15 @@ | |||
1 | #include <opkele/exception.h> | 1 | #include <opkele/exception.h> |
2 | #include <opkele/extension.h> | 2 | #include <opkele/extension.h> |
3 | 3 | ||
4 | namespace opkele { | 4 | 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"); |
14 | } | 14 | } |
15 | } | 15 | } |
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 | |||
@@ -1,16 +1,16 @@ | |||
1 | #include <cstdarg> | 1 | #include <cstdarg> |
2 | #include <opkele/extension_chain.h> | 2 | #include <opkele/extension_chain.h> |
3 | 3 | ||
4 | namespace opkele { | 4 | 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 | } |
15 | 15 | ||
16 | } | 16 | } |
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 | |||
@@ -1,121 +1,30 @@ | |||
1 | #include <opkele/types.h> | 1 | #include <opkele/types.h> |
2 | #include <opkele/exception.h> | 2 | #include <opkele/exception.h> |
3 | #include <opkele/util.h> | 3 | #include <opkele/util.h> |
4 | #include <openssl/sha.h> | 4 | #include <openssl/sha.h> |
5 | #include <openssl/hmac.h> | 5 | #include <openssl/hmac.h> |
6 | 6 | ||
7 | #include "config.h" | 7 | #include "config.h" |
8 | 8 | ||
9 | namespace opkele { | 9 | namespace opkele { |
10 | using namespace std; | 10 | using namespace std; |
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 { |
83 | string rv = url; | 13 | string rv = url; |
84 | bool p = true; | 14 | bool p = true; |
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) |
91 | rv += '&'; | 19 | rv += '&'; |
92 | else | 20 | else |
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 | } |
99 | return rv; | 27 | return rv; |
100 | } | 28 | } |
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 | |||
@@ -1,171 +1,171 @@ | |||
1 | #include <cstring> | 1 | #include <cstring> |
2 | #include <vector> | 2 | #include <vector> |
3 | #include <openssl/sha.h> | 3 | #include <openssl/sha.h> |
4 | #include <openssl/hmac.h> | 4 | #include <openssl/hmac.h> |
5 | #include <opkele/util.h> | 5 | #include <opkele/util.h> |
6 | #include <opkele/exception.h> | 6 | #include <opkele/exception.h> |
7 | #include <opkele/server.h> | 7 | #include <opkele/server.h> |
8 | #include <opkele/data.h> | 8 | #include <opkele/data.h> |
9 | 9 | ||
10 | namespace opkele { | 10 | namespace opkele { |
11 | using namespace std; | 11 | using namespace std; |
12 | 12 | ||
13 | void server_t::associate(const params_t& pin,params_t& pout) { | 13 | void server_t::associate(const params_t& pin,params_t& pout) { |
14 | util::dh_t dh; | 14 | util::dh_t dh; |
15 | util::bignum_t c_pub; | 15 | util::bignum_t c_pub; |
16 | unsigned char key_sha1[SHA_DIGEST_LENGTH]; | 16 | unsigned char key_sha1[SHA_DIGEST_LENGTH]; |
17 | enum { | 17 | enum { |
18 | sess_cleartext, | 18 | sess_cleartext, |
19 | sess_dh_sha1 | 19 | sess_dh_sha1 |
20 | } st = sess_cleartext; | 20 | } st = sess_cleartext; |
21 | if( | 21 | if( |
22 | pin.has_param("openid.session_type") | 22 | pin.has_param("openid.session_type") |
23 | && pin.get_param("openid.session_type")=="DH-SHA1" ) { | 23 | && pin.get_param("openid.session_type")=="DH-SHA1" ) { |
24 | /* TODO: fallback to cleartext in case of exceptions here? */ | 24 | /* TODO: fallback to cleartext in case of exceptions here? */ |
25 | if(!(dh = DH_new())) | 25 | if(!(dh = DH_new())) |
26 | throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); | 26 | throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); |
27 | c_pub = util::base64_to_bignum(pin.get_param("openid.dh_consumer_public")); | 27 | c_pub = util::base64_to_bignum(pin.get_param("openid.dh_consumer_public")); |
28 | if(pin.has_param("openid.dh_modulus")) | 28 | if(pin.has_param("openid.dh_modulus")) |
29 | dh->p = util::base64_to_bignum(pin.get_param("openid.dh_modulus")); | 29 | dh->p = util::base64_to_bignum(pin.get_param("openid.dh_modulus")); |
30 | else | 30 | else |
31 | dh->p = util::dec_to_bignum(data::_default_p); | 31 | dh->p = util::dec_to_bignum(data::_default_p); |
32 | if(pin.has_param("openid.dh_gen")) | 32 | if(pin.has_param("openid.dh_gen")) |
33 | dh->g = util::base64_to_bignum(pin.get_param("openid.dh_gen")); | 33 | dh->g = util::base64_to_bignum(pin.get_param("openid.dh_gen")); |
34 | else | 34 | else |
35 | dh->g = util::dec_to_bignum(data::_default_g); | 35 | dh->g = util::dec_to_bignum(data::_default_g); |
36 | if(!DH_generate_key(dh)) | 36 | if(!DH_generate_key(dh)) |
37 | throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); | 37 | throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); |
38 | vector<unsigned char> ck(DH_size(dh)+1); | 38 | vector<unsigned char> ck(DH_size(dh)+1); |
39 | unsigned char *ckptr = &(ck.front())+1; | 39 | unsigned char *ckptr = &(ck.front())+1; |
40 | int cklen = DH_compute_key(ckptr,c_pub,dh); | 40 | int cklen = DH_compute_key(ckptr,c_pub,dh); |
41 | if(cklen<0) | 41 | if(cklen<0) |
42 | throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); | 42 | throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); |
43 | if(cklen && (*ckptr)&0x80) { | 43 | if(cklen && (*ckptr)&0x80) { |
44 | (*(--ckptr)) = 0; ++cklen; | 44 | (*(--ckptr)) = 0; ++cklen; |
45 | } | 45 | } |
46 | SHA1(ckptr,cklen,key_sha1); | 46 | SHA1(ckptr,cklen,key_sha1); |
47 | st = sess_dh_sha1; | 47 | st = sess_dh_sha1; |
48 | } | 48 | } |
49 | assoc_t assoc = alloc_assoc(mode_associate); | 49 | assoc_t assoc = alloc_assoc(mode_associate); |
50 | time_t now = time(0); | 50 | time_t now = time(0); |
51 | pout.clear(); | 51 | pout.clear(); |
52 | pout["assoc_type"] = assoc->assoc_type(); | 52 | pout["assoc_type"] = assoc->assoc_type(); |
53 | pout["assoc_handle"] = assoc->handle(); | 53 | pout["assoc_handle"] = assoc->handle(); |
54 | /* TODO: eventually remove deprecated stuff */ | 54 | /* TODO: eventually remove deprecated stuff */ |
55 | pout["issued"] = util::time_to_w3c(now); | 55 | pout["issued"] = util::time_to_w3c(now); |
56 | pout["expiry"] = util::time_to_w3c(now+assoc->expires_in()); | 56 | pout["expiry"] = util::time_to_w3c(now+assoc->expires_in()); |
57 | pout["expires_in"] = util::long_to_string(assoc->expires_in()); | 57 | pout["expires_in"] = util::long_to_string(assoc->expires_in()); |
58 | secret_t secret = assoc->secret(); | 58 | secret_t secret = assoc->secret(); |
59 | switch(st) { | 59 | switch(st) { |
60 | case sess_dh_sha1: | 60 | case sess_dh_sha1: |
61 | pout["session_type"] = "DH-SHA1"; | 61 | pout["session_type"] = "DH-SHA1"; |
62 | pout["dh_server_public"] = util::bignum_to_base64(dh->pub_key); | 62 | pout["dh_server_public"] = util::bignum_to_base64(dh->pub_key); |
63 | secret.enxor_to_base64(key_sha1,pout["enc_mac_key"]); | 63 | secret.enxor_to_base64(key_sha1,pout["enc_mac_key"]); |
64 | break; | 64 | break; |
65 | default: | 65 | default: |
66 | secret.to_base64(pout["mac_key"]); | 66 | secret.to_base64(pout["mac_key"]); |
67 | break; | 67 | break; |
68 | } | 68 | } |
69 | } | 69 | } |
70 | 70 | ||
71 | void server_t::checkid_immediate(const params_t& pin,string& return_to,params_t& pout,extension_t *ext) { | 71 | void server_t::checkid_immediate(const params_t& pin,string& return_to,params_t& pout,extension_t *ext) { |
72 | checkid_(mode_checkid_immediate,pin,return_to,pout,ext); | 72 | checkid_(mode_checkid_immediate,pin,return_to,pout,ext); |
73 | } | 73 | } |
74 | 74 | ||
75 | void server_t::checkid_setup(const params_t& pin,string& return_to,params_t& pout,extension_t *ext) { | 75 | void server_t::checkid_setup(const params_t& pin,string& return_to,params_t& pout,extension_t *ext) { |
76 | checkid_(mode_checkid_setup,pin,return_to,pout,ext); | 76 | checkid_(mode_checkid_setup,pin,return_to,pout,ext); |
77 | } | 77 | } |
78 | 78 | ||
79 | void server_t::checkid_(mode_t mode,const params_t& pin,string& return_to,params_t& pout,extension_t *ext) { | 79 | void server_t::checkid_(mode_t mode,const params_t& pin,string& return_to,params_t& pout,extension_t *ext) { |
80 | if(mode!=mode_checkid_immediate && mode!=mode_checkid_setup) | 80 | if(mode!=mode_checkid_immediate && mode!=mode_checkid_setup) |
81 | throw bad_input(OPKELE_CP_ "invalid checkid_* mode"); | 81 | throw bad_input(OPKELE_CP_ "invalid checkid_* mode"); |
82 | pout.clear(); | 82 | pout.clear(); |
83 | assoc_t assoc; | 83 | assoc_t assoc; |
84 | try { | 84 | try { |
85 | assoc = retrieve_assoc(pin.get_param("openid.assoc_handle")); | 85 | assoc = retrieve_assoc(pin.get_param("openid.assoc_handle")); |
86 | }catch(failed_lookup& fl) { | 86 | }catch(failed_lookup& fl) { |
87 | // no handle specified or no valid handle found, going dumb | 87 | // no handle specified or no valid handle found, going dumb |
88 | assoc = alloc_assoc(mode_checkid_setup); | 88 | assoc = alloc_assoc(mode_checkid_setup); |
89 | if(pin.has_param("openid.assoc_handle")) | 89 | if(pin.has_param("openid.assoc_handle")) |
90 | pout["invalidate_handle"]=pin.get_param("openid.assoc_handle"); | 90 | pout["invalidate_handle"]=pin.get_param("openid.assoc_handle"); |
91 | } | 91 | } |
92 | string trust_root; | 92 | string trust_root; |
93 | try { | 93 | try { |
94 | trust_root = pin.get_param("openid.trust_root"); | 94 | trust_root = pin.get_param("openid.trust_root"); |
95 | }catch(failed_lookup& fl) { } | 95 | }catch(failed_lookup& fl) { } |
96 | string identity = pin.get_param("openid.identity"); | 96 | string identity = pin.get_param("openid.identity"); |
97 | return_to = pin.get_param("openid.return_to"); | 97 | return_to = pin.get_param("openid.return_to"); |
98 | validate(*assoc,pin,identity,trust_root); | 98 | validate(*assoc,pin,identity,trust_root); |
99 | pout["mode"] = "id_res"; | 99 | pout["mode"] = "id_res"; |
100 | pout["assoc_handle"] = assoc->handle(); | 100 | pout["assoc_handle"] = assoc->handle(); |
101 | if(pin.has_param("openid.assoc_handle") && assoc->stateless()) | 101 | if(pin.has_param("openid.assoc_handle") && assoc->stateless()) |
102 | pout["invalidate_handle"] = pin.get_param("openid.assoc_handle"); | 102 | pout["invalidate_handle"] = pin.get_param("openid.assoc_handle"); |
103 | pout["identity"] = identity; | 103 | pout["identity"] = identity; |
104 | pout["return_to"] = return_to; | 104 | pout["return_to"] = return_to; |
105 | /* TODO: eventually remove deprecated stuff */ | 105 | /* TODO: eventually remove deprecated stuff */ |
106 | time_t now = time(0); | 106 | time_t now = time(0); |
107 | pout["issued"] = util::time_to_w3c(now); | 107 | pout["issued"] = util::time_to_w3c(now); |
108 | pout["valid_to"] = util::time_to_w3c(now+120); | 108 | pout["valid_to"] = util::time_to_w3c(now+120); |
109 | pout["exipres_in"] = "120"; | 109 | pout["exipres_in"] = "120"; |
110 | pout["signed"]="mode,identity,return_to"; | 110 | pout["signed"]="mode,identity,return_to"; |
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 | } |
114 | 114 | ||
115 | void server_t::check_authentication(const params_t& pin,params_t& pout) { | 115 | void server_t::check_authentication(const params_t& pin,params_t& pout) { |
116 | vector<unsigned char> sig; | 116 | vector<unsigned char> sig; |
117 | const string& sigenc = pin.get_param("openid.sig"); | 117 | const string& sigenc = pin.get_param("openid.sig"); |
118 | util::decode_base64(sigenc,sig); | 118 | util::decode_base64(sigenc,sig); |
119 | assoc_t assoc; | 119 | assoc_t assoc; |
120 | try { | 120 | try { |
121 | assoc = retrieve_assoc(pin.get_param("openid.assoc_handle")); | 121 | assoc = retrieve_assoc(pin.get_param("openid.assoc_handle")); |
122 | }catch(failed_lookup& fl) { | 122 | }catch(failed_lookup& fl) { |
123 | throw failed_assertion(OPKELE_CP_ "invalid handle or handle not specified"); | 123 | throw failed_assertion(OPKELE_CP_ "invalid handle or handle not specified"); |
124 | } | 124 | } |
125 | if(!assoc->stateless()) | 125 | if(!assoc->stateless()) |
126 | throw stateful_handle(OPKELE_CP_ "will not do check_authentication on a stateful handle"); | 126 | throw stateful_handle(OPKELE_CP_ "will not do check_authentication on a stateful handle"); |
127 | const string& slist = pin.get_param("openid.signed"); | 127 | const string& slist = pin.get_param("openid.signed"); |
128 | string kv; | 128 | string kv; |
129 | string::size_type p =0; | 129 | string::size_type p =0; |
130 | while(true) { | 130 | while(true) { |
131 | string::size_type co = slist.find(',',p); | 131 | string::size_type co = slist.find(',',p); |
132 | string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p); | 132 | string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p); |
133 | kv += f; | 133 | kv += f; |
134 | kv += ':'; | 134 | kv += ':'; |
135 | if(f=="mode") | 135 | if(f=="mode") |
136 | kv += "id_res"; | 136 | kv += "id_res"; |
137 | else { | 137 | else { |
138 | f.insert(0,"openid."); | 138 | f.insert(0,"openid."); |
139 | kv += pin.get_param(f); | 139 | kv += pin.get_param(f); |
140 | } | 140 | } |
141 | kv += '\n'; | 141 | kv += '\n'; |
142 | if(co==string::npos) | 142 | if(co==string::npos) |
143 | break; | 143 | break; |
144 | p = co+1; | 144 | p = co+1; |
145 | } | 145 | } |
146 | secret_t secret = assoc->secret(); | 146 | secret_t secret = assoc->secret(); |
147 | unsigned int md_len = 0; | 147 | unsigned int md_len = 0; |
148 | unsigned char *md = HMAC( | 148 | unsigned char *md = HMAC( |
149 | EVP_sha1(), | 149 | EVP_sha1(), |
150 | &(secret.front()),secret.size(), | 150 | &(secret.front()),secret.size(), |
151 | (const unsigned char *)kv.data(),kv.length(), | 151 | (const unsigned char *)kv.data(),kv.length(), |
152 | 0,&md_len); | 152 | 0,&md_len); |
153 | pout.clear(); | 153 | pout.clear(); |
154 | if(sig.size()==md_len && !memcmp(&(sig.front()),md,md_len)) { | 154 | if(sig.size()==md_len && !memcmp(&(sig.front()),md,md_len)) { |
155 | pout["is_valid"]="true"; | 155 | pout["is_valid"]="true"; |
156 | pout["lifetime"]="60"; /* TODO: eventually remove deprecated stuff */ | 156 | pout["lifetime"]="60"; /* TODO: eventually remove deprecated stuff */ |
157 | }else{ | 157 | }else{ |
158 | pout["is_valid"]="false"; | 158 | pout["is_valid"]="false"; |
159 | pout["lifetime"]="0"; /* TODO: eventually remove deprecated stuff */ | 159 | pout["lifetime"]="0"; /* TODO: eventually remove deprecated stuff */ |
160 | } | 160 | } |
161 | if(pin.has_param("openid.invalidate_handle")) { | 161 | if(pin.has_param("openid.invalidate_handle")) { |
162 | string h = pin.get_param("openid.invalidate_handle"); | 162 | string h = pin.get_param("openid.invalidate_handle"); |
163 | try { | 163 | try { |
164 | assoc_t tmp = retrieve_assoc(h); | 164 | assoc_t tmp = retrieve_assoc(h); |
165 | }catch(invalid_handle& ih) { | 165 | }catch(invalid_handle& ih) { |
166 | pout["invalidate_handle"] = h; | 166 | pout["invalidate_handle"] = h; |
167 | }catch(failed_lookup& fl) { } | 167 | }catch(failed_lookup& fl) { } |
168 | } | 168 | } |
169 | } | 169 | } |
170 | 170 | ||
171 | } | 171 | } |
diff --git a/lib/sreg.cc b/lib/sreg.cc index 03edf57..7e2d588 100644 --- a/lib/sreg.cc +++ b/lib/sreg.cc | |||
@@ -1,126 +1,140 @@ | |||
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 | ||
6 | namespace opkele { | 6 | namespace 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(params_t& p,const string& /* identity */) { | 31 | void sreg_t::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 | 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 | } |
57 | } | 68 | } |
58 | 69 | ||
59 | const string& sreg_t::get_field(fieldbit_t fb) const { | 70 | const string& sreg_t::get_field(fieldbit_t fb) const { |
60 | response_t::const_iterator i = response.find(fb); | 71 | response_t::const_iterator i = response.find(fb); |
61 | if(i==response.end()) | 72 | if(i==response.end()) |
62 | throw failed_lookup(OPKELE_CP_ "no field data available"); | 73 | throw failed_lookup(OPKELE_CP_ "no field data available"); |
63 | return i->second; | 74 | return i->second; |
64 | } | 75 | } |
65 | 76 | ||
66 | void sreg_t::set_field(fieldbit_t fb,const string& fv) { | 77 | void sreg_t::set_field(fieldbit_t fb,const string& fv) { |
67 | response[fb] = fv; | 78 | response[fb] = fv; |
68 | has_fields |= fb; | 79 | has_fields |= fb; |
69 | } | 80 | } |
70 | 81 | ||
71 | void sreg_t::reset_field(fieldbit_t fb) { | 82 | void sreg_t::reset_field(fieldbit_t fb) { |
72 | has_fields &= ~fb; | 83 | has_fields &= ~fb; |
73 | response.erase(fb); | 84 | response.erase(fb); |
74 | } | 85 | } |
75 | 86 | ||
76 | void sreg_t::clear() { | 87 | void sreg_t::clear() { |
77 | has_fields = 0; response.clear(); | 88 | has_fields = 0; response.clear(); |
78 | } | 89 | } |
79 | 90 | ||
80 | static long fields_list_to_bitmask(string& fl) { | 91 | static long fields_list_to_bitmask(string& fl) { |
81 | long rv = 0; | 92 | long rv = 0; |
82 | while(!fl.empty()) { | 93 | while(!fl.empty()) { |
83 | string::size_type co = fl.find(','); | 94 | string::size_type co = fl.find(','); |
84 | string fn; | 95 | string fn; |
85 | if(co==string::npos) { | 96 | if(co==string::npos) { |
86 | fn = fl; fl.erase(); | 97 | fn = fl; fl.erase(); |
87 | }else{ | 98 | }else{ |
88 | fn = fl.substr(0,co); fl.erase(0,co+1); | 99 | fn = fl.substr(0,co); fl.erase(0,co+1); |
89 | } | 100 | } |
90 | fields_iterator f = find(fields_BEGIN,fields_END,fn); | 101 | fields_iterator f = find(fields_BEGIN,fields_END,fn); |
91 | if(f!=fields_END) | 102 | if(f!=fields_END) |
92 | rv |= f->fieldbit; | 103 | rv |= f->fieldbit; |
93 | } | 104 | } |
94 | return rv; | 105 | return rv; |
95 | } | 106 | } |
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(); |
99 | fields_response = 0; | 111 | fields_response = 0; |
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); |
103 | }catch(failed_lookup&) { } | 115 | }catch(failed_lookup&) { } |
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); |
107 | }catch(failed_lookup&) { } | 119 | }catch(failed_lookup&) { } |
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; |
125 | } | 139 | } |
126 | } | 140 | } |
diff --git a/lib/util.cc b/lib/util.cc index a9b9bed..54d6535 100644 --- a/lib/util.cc +++ b/lib/util.cc | |||
@@ -1,316 +1,383 @@ | |||
1 | #include <errno.h> | 1 | #include <errno.h> |
2 | #include <cassert> | 2 | #include <cassert> |
3 | #include <cctype> | 3 | #include <cctype> |
4 | #include <cstring> | 4 | #include <cstring> |
5 | #include <vector> | 5 | #include <vector> |
6 | #include <string> | 6 | #include <string> |
7 | #include <stack> | 7 | #include <stack> |
8 | #include <openssl/bio.h> | 8 | #include <openssl/bio.h> |
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> |
11 | #include "opkele/util.h" | 12 | #include "opkele/util.h" |
12 | #include "opkele/exception.h" | 13 | #include "opkele/exception.h" |
13 | 14 | ||
15 | #include <config.h> | ||
16 | #ifdef HAVE_DEMANGLE | ||
17 | # include <cxxabi.h> | ||
18 | #endif | ||
19 | |||
14 | namespace opkele { | 20 | namespace opkele { |
15 | using namespace std; | 21 | using namespace std; |
16 | 22 | ||
17 | namespace util { | 23 | namespace util { |
18 | 24 | ||
19 | /* | 25 | /* |
20 | * base64 | 26 | * base64 |
21 | */ | 27 | */ |
22 | string encode_base64(const void *data,size_t length) { | 28 | string encode_base64(const void *data,size_t length) { |
23 | BIO *b64 = 0, *bmem = 0; | 29 | BIO *b64 = 0, *bmem = 0; |
24 | try { | 30 | try { |
25 | b64 = BIO_new(BIO_f_base64()); | 31 | b64 = BIO_new(BIO_f_base64()); |
26 | if(!b64) | 32 | if(!b64) |
27 | throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 encoder"); | 33 | throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 encoder"); |
28 | BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL); | 34 | BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL); |
29 | bmem = BIO_new(BIO_s_mem()); | 35 | bmem = BIO_new(BIO_s_mem()); |
30 | BIO_set_flags(b64,BIO_CLOSE); | 36 | BIO_set_flags(b64,BIO_CLOSE); |
31 | if(!bmem) | 37 | if(!bmem) |
32 | throw exception_openssl(OPKELE_CP_ "failed to BIO_new() memory buffer"); | 38 | throw exception_openssl(OPKELE_CP_ "failed to BIO_new() memory buffer"); |
33 | BIO_push(b64,bmem); | 39 | BIO_push(b64,bmem); |
34 | if(((size_t)BIO_write(b64,data,length))!=length) | 40 | if(((size_t)BIO_write(b64,data,length))!=length) |
35 | throw exception_openssl(OPKELE_CP_ "failed to BIO_write()"); | 41 | throw exception_openssl(OPKELE_CP_ "failed to BIO_write()"); |
36 | if(BIO_flush(b64)!=1) | 42 | if(BIO_flush(b64)!=1) |
37 | throw exception_openssl(OPKELE_CP_ "failed to BIO_flush()"); | 43 | throw exception_openssl(OPKELE_CP_ "failed to BIO_flush()"); |
38 | char *rvd; | 44 | char *rvd; |
39 | long rvl = BIO_get_mem_data(bmem,&rvd); | 45 | long rvl = BIO_get_mem_data(bmem,&rvd); |
40 | string rv(rvd,rvl); | 46 | string rv(rvd,rvl); |
41 | BIO_free_all(b64); | 47 | BIO_free_all(b64); |
42 | return rv; | 48 | return rv; |
43 | }catch(...) { | 49 | }catch(...) { |
44 | if(b64) BIO_free_all(b64); | 50 | if(b64) BIO_free_all(b64); |
45 | throw; | 51 | throw; |
46 | } | 52 | } |
47 | } | 53 | } |
48 | 54 | ||
49 | void decode_base64(const string& data,vector<unsigned char>& rv) { | 55 | void decode_base64(const string& data,vector<unsigned char>& rv) { |
50 | BIO *b64 = 0, *bmem = 0; | 56 | BIO *b64 = 0, *bmem = 0; |
51 | rv.clear(); | 57 | rv.clear(); |
52 | try { | 58 | try { |
53 | bmem = BIO_new_mem_buf((void*)data.data(),data.size()); | 59 | bmem = BIO_new_mem_buf((void*)data.data(),data.size()); |
54 | if(!bmem) | 60 | if(!bmem) |
55 | throw exception_openssl(OPKELE_CP_ "failed to BIO_new_mem_buf()"); | 61 | throw exception_openssl(OPKELE_CP_ "failed to BIO_new_mem_buf()"); |
56 | b64 = BIO_new(BIO_f_base64()); | 62 | b64 = BIO_new(BIO_f_base64()); |
57 | if(!b64) | 63 | if(!b64) |
58 | throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 decoder"); | 64 | throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 decoder"); |
59 | BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL); | 65 | BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL); |
60 | BIO_push(b64,bmem); | 66 | BIO_push(b64,bmem); |
61 | unsigned char tmp[512]; | 67 | unsigned char tmp[512]; |
62 | size_t rb = 0; | 68 | size_t rb = 0; |
63 | while((rb=BIO_read(b64,tmp,sizeof(tmp)))>0) | 69 | while((rb=BIO_read(b64,tmp,sizeof(tmp)))>0) |
64 | rv.insert(rv.end(),tmp,&tmp[rb]); | 70 | rv.insert(rv.end(),tmp,&tmp[rb]); |
65 | BIO_free_all(b64); | 71 | BIO_free_all(b64); |
66 | }catch(...) { | 72 | }catch(...) { |
67 | if(b64) BIO_free_all(b64); | 73 | if(b64) BIO_free_all(b64); |
68 | throw; | 74 | throw; |
69 | } | 75 | } |
70 | } | 76 | } |
71 | 77 | ||
72 | /* | 78 | /* |
73 | * big numerics | 79 | * big numerics |
74 | */ | 80 | */ |
75 | 81 | ||
76 | BIGNUM *base64_to_bignum(const string& b64) { | 82 | BIGNUM *base64_to_bignum(const string& b64) { |
77 | vector<unsigned char> bin; | 83 | vector<unsigned char> bin; |
78 | decode_base64(b64,bin); | 84 | decode_base64(b64,bin); |
79 | BIGNUM *rv = BN_bin2bn(&(bin.front()),bin.size(),0); | 85 | BIGNUM *rv = BN_bin2bn(&(bin.front()),bin.size(),0); |
80 | if(!rv) | 86 | if(!rv) |
81 | throw failed_conversion(OPKELE_CP_ "failed to BN_bin2bn()"); | 87 | throw failed_conversion(OPKELE_CP_ "failed to BN_bin2bn()"); |
82 | return rv; | 88 | return rv; |
83 | } | 89 | } |
84 | 90 | ||
85 | BIGNUM *dec_to_bignum(const string& dec) { | 91 | BIGNUM *dec_to_bignum(const string& dec) { |
86 | BIGNUM *rv = 0; | 92 | BIGNUM *rv = 0; |
87 | if(!BN_dec2bn(&rv,dec.c_str())) | 93 | if(!BN_dec2bn(&rv,dec.c_str())) |
88 | throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()"); | 94 | throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()"); |
89 | return rv; | 95 | return rv; |
90 | } | 96 | } |
91 | 97 | ||
92 | string bignum_to_base64(const BIGNUM *bn) { | 98 | string bignum_to_base64(const BIGNUM *bn) { |
93 | vector<unsigned char> bin(BN_num_bytes(bn)+1); | 99 | vector<unsigned char> bin(BN_num_bytes(bn)+1); |
94 | unsigned char *binptr = &(bin.front())+1; | 100 | unsigned char *binptr = &(bin.front())+1; |
95 | int l = BN_bn2bin(bn,binptr); | 101 | int l = BN_bn2bin(bn,binptr); |
96 | if(l && (*binptr)&0x80){ | 102 | if(l && (*binptr)&0x80){ |
97 | (*(--binptr)) = 0; ++l; | 103 | (*(--binptr)) = 0; ++l; |
98 | } | 104 | } |
99 | return encode_base64(binptr,l); | 105 | return encode_base64(binptr,l); |
100 | } | 106 | } |
101 | 107 | ||
102 | /* | 108 | /* |
103 | * w3c times | 109 | * w3c times |
104 | */ | 110 | */ |
105 | 111 | ||
106 | string time_to_w3c(time_t t) { | 112 | string time_to_w3c(time_t t) { |
107 | struct tm tm_t; | 113 | struct tm tm_t; |
108 | if(!gmtime_r(&t,&tm_t)) | 114 | if(!gmtime_r(&t,&tm_t)) |
109 | throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()"); | 115 | throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()"); |
110 | char rv[25]; | 116 | char rv[25]; |
111 | if(!strftime(rv,sizeof(rv)-1,"%Y-%m-%dT%H:%M:%SZ",&tm_t)) | 117 | if(!strftime(rv,sizeof(rv)-1,"%Y-%m-%dT%H:%M:%SZ",&tm_t)) |
112 | throw failed_conversion(OPKELE_CP_ "failed to strftime()"); | 118 | throw failed_conversion(OPKELE_CP_ "failed to strftime()"); |
113 | return rv; | 119 | return rv; |
114 | } | 120 | } |
115 | 121 | ||
116 | time_t w3c_to_time(const string& w) { | 122 | time_t w3c_to_time(const string& w) { |
117 | int fraction; | 123 | int fraction; |
118 | struct tm tm_t; | 124 | struct tm tm_t; |
119 | memset(&tm_t,0,sizeof(tm_t)); | 125 | memset(&tm_t,0,sizeof(tm_t)); |
120 | if( ( | 126 | if( ( |
121 | sscanf( | 127 | sscanf( |
122 | w.c_str(), | 128 | w.c_str(), |
123 | "%04d-%02d-%02dT%02d:%02d:%02dZ", | 129 | "%04d-%02d-%02dT%02d:%02d:%02dZ", |
124 | &tm_t.tm_year,&tm_t.tm_mon,&tm_t.tm_mday, | 130 | &tm_t.tm_year,&tm_t.tm_mon,&tm_t.tm_mday, |
125 | &tm_t.tm_hour,&tm_t.tm_min,&tm_t.tm_sec | 131 | &tm_t.tm_hour,&tm_t.tm_min,&tm_t.tm_sec |
126 | ) != 6 | 132 | ) != 6 |
127 | ) && ( | 133 | ) && ( |
128 | sscanf( | 134 | sscanf( |
129 | w.c_str(), | 135 | w.c_str(), |
130 | "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", | 136 | "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", |
131 | &tm_t.tm_year,&tm_t.tm_mon,&tm_t.tm_mday, | 137 | &tm_t.tm_year,&tm_t.tm_mon,&tm_t.tm_mday, |
132 | &tm_t.tm_hour,&tm_t.tm_min,&tm_t.tm_sec, | 138 | &tm_t.tm_hour,&tm_t.tm_min,&tm_t.tm_sec, |
133 | &fraction | 139 | &fraction |
134 | ) != 7 | 140 | ) != 7 |
135 | ) ) | 141 | ) ) |
136 | throw failed_conversion(OPKELE_CP_ "failed to sscanf()"); | 142 | throw failed_conversion(OPKELE_CP_ "failed to sscanf()"); |
137 | tm_t.tm_mon--; | 143 | tm_t.tm_mon--; |
138 | tm_t.tm_year-=1900; | 144 | tm_t.tm_year-=1900; |
139 | time_t rv = mktime(&tm_t); | 145 | time_t rv = mktime(&tm_t); |
140 | if(rv==(time_t)-1) | 146 | if(rv==(time_t)-1) |
141 | throw failed_conversion(OPKELE_CP_ "failed to mktime()"); | 147 | throw failed_conversion(OPKELE_CP_ "failed to mktime()"); |
142 | return rv-timezone; | 148 | return rv-timezone; |
143 | } | 149 | } |
144 | 150 | ||
145 | /* | 151 | /* |
146 | * | 152 | * |
147 | */ | 153 | */ |
148 | 154 | ||
149 | string url_encode(const string& str) { | 155 | string url_encode(const string& str) { |
150 | char * t = curl_escape(str.c_str(),str.length()); | 156 | char * t = curl_escape(str.c_str(),str.length()); |
151 | if(!t) | 157 | if(!t) |
152 | throw failed_conversion(OPKELE_CP_ "failed to curl_escape()"); | 158 | throw failed_conversion(OPKELE_CP_ "failed to curl_escape()"); |
153 | string rv(t); | 159 | string rv(t); |
154 | curl_free(t); | 160 | curl_free(t); |
155 | return rv; | 161 | return rv; |
156 | } | 162 | } |
157 | 163 | ||
158 | string long_to_string(long l) { | 164 | string long_to_string(long l) { |
159 | char rv[32]; | 165 | char rv[32]; |
160 | int r=snprintf(rv,sizeof(rv),"%ld",l); | 166 | int r=snprintf(rv,sizeof(rv),"%ld",l); |
161 | if(r<0 || r>=(int)sizeof(rv)) | 167 | if(r<0 || r>=(int)sizeof(rv)) |
162 | throw failed_conversion(OPKELE_CP_ "failed to snprintf()"); | 168 | throw failed_conversion(OPKELE_CP_ "failed to snprintf()"); |
163 | return rv; | 169 | return rv; |
164 | } | 170 | } |
165 | 171 | ||
166 | long string_to_long(const string& s) { | 172 | long string_to_long(const string& s) { |
167 | char *endptr = 0; | 173 | char *endptr = 0; |
168 | long rv = strtol(s.c_str(),&endptr,10); | 174 | long rv = strtol(s.c_str(),&endptr,10); |
169 | if((!endptr) || endptr==s.c_str()) | 175 | if((!endptr) || endptr==s.c_str()) |
170 | throw failed_conversion(OPKELE_CP_ "failed to strtol()"); | 176 | throw failed_conversion(OPKELE_CP_ "failed to strtol()"); |
171 | return rv; | 177 | return rv; |
172 | } | 178 | } |
173 | 179 | ||
174 | /* | 180 | /* |
175 | * Normalize URL according to the rules, described in rfc 3986, section 6 | 181 | * Normalize URL according to the rules, described in rfc 3986, section 6 |
176 | * | 182 | * |
177 | * - uppercase hex triplets (e.g. %ab -> %AB) | 183 | * - uppercase hex triplets (e.g. %ab -> %AB) |
178 | * - lowercase scheme and host | 184 | * - lowercase scheme and host |
179 | * - decode %-encoded characters, specified as unreserved in rfc 3986, section 2.3, | 185 | * - decode %-encoded characters, specified as unreserved in rfc 3986, section 2.3, |
180 | * that is - [:alpha:][:digit:]._~- | 186 | * that is - [:alpha:][:digit:]._~- |
181 | * - remove dot segments | 187 | * - remove dot segments |
182 | * - remove empty and default ports | 188 | * - remove empty and default ports |
183 | * - if there's no path component, add '/' | 189 | * - if there's no path component, add '/' |
184 | */ | 190 | */ |
185 | string rfc_3986_normalize_uri(const string& uri) { | 191 | string rfc_3986_normalize_uri(const string& uri) { |
186 | static const char *whitespace = " \t\r\n"; | 192 | static const char *whitespace = " \t\r\n"; |
187 | string rv; | 193 | string rv; |
188 | string::size_type ns = uri.find_first_not_of(whitespace); | 194 | string::size_type ns = uri.find_first_not_of(whitespace); |
189 | if(ns==string::npos) | 195 | if(ns==string::npos) |
190 | throw bad_input(OPKELE_CP_ "Can't normalize empty URI"); | 196 | throw bad_input(OPKELE_CP_ "Can't normalize empty URI"); |
191 | string::size_type colon = uri.find(':',ns); | 197 | string::size_type colon = uri.find(':',ns); |
192 | if(colon==string::npos) | 198 | if(colon==string::npos) |
193 | throw bad_input(OPKELE_CP_ "No scheme specified in URI"); | 199 | throw bad_input(OPKELE_CP_ "No scheme specified in URI"); |
194 | transform( | 200 | transform( |
195 | uri.begin()+ns, uri.begin()+colon+1, | 201 | uri.begin()+ns, uri.begin()+colon+1, |
196 | back_inserter(rv), ::tolower ); | 202 | back_inserter(rv), ::tolower ); |
197 | bool s; | 203 | bool s; |
198 | string::size_type ul = uri.find_last_not_of(whitespace)+1; | 204 | string::size_type ul = uri.find_last_not_of(whitespace)+1; |
199 | if(ul <= (colon+3)) | 205 | if(ul <= (colon+3)) |
200 | throw bad_input(OPKELE_CP_ "Unexpected end of URI being normalized encountered"); | 206 | throw bad_input(OPKELE_CP_ "Unexpected end of URI being normalized encountered"); |
201 | if(uri[colon+1]!='/' || uri[colon+2]!='/') | 207 | if(uri[colon+1]!='/' || uri[colon+2]!='/') |
202 | throw bad_input(OPKELE_CP_ "Unexpected input in URI being normalized after scheme component"); | 208 | throw bad_input(OPKELE_CP_ "Unexpected input in URI being normalized after scheme component"); |
203 | if(rv=="http:") | 209 | if(rv=="http:") |
204 | s = false; | 210 | s = false; |
205 | else if(rv=="https:") | 211 | else if(rv=="https:") |
206 | s = true; | 212 | s = true; |
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? |
211 | */ | 216 | */ |
212 | rv.append(uri,colon+1,ul-colon-1); | 217 | rv.append(uri,colon+1,ul-colon-1); |
213 | return rv; | 218 | return rv; |
214 | } | 219 | } |
215 | rv += "//"; | 220 | rv += "//"; |
216 | string::size_type interesting = uri.find_first_of(":/#?",colon+3); | 221 | string::size_type interesting = uri.find_first_of(":/#?",colon+3); |
217 | if(interesting==string::npos) { | 222 | if(interesting==string::npos) { |
218 | transform( | 223 | transform( |
219 | uri.begin()+colon+3,uri.begin()+ul, | 224 | uri.begin()+colon+3,uri.begin()+ul, |
220 | back_inserter(rv), ::tolower ); | 225 | back_inserter(rv), ::tolower ); |
221 | rv += '/'; return rv; | 226 | rv += '/'; return rv; |
222 | } | 227 | } |
223 | transform( | 228 | transform( |
224 | uri.begin()+colon+3,uri.begin()+interesting, | 229 | uri.begin()+colon+3,uri.begin()+interesting, |
225 | back_inserter(rv), ::tolower ); | 230 | back_inserter(rv), ::tolower ); |
226 | bool qf = false; | 231 | bool qf = false; |
227 | char ic = uri[interesting]; | 232 | char ic = uri[interesting]; |
228 | if(ic==':') { | 233 | if(ic==':') { |
229 | string::size_type ni = uri.find_first_of("/#?%",interesting+1); | 234 | string::size_type ni = uri.find_first_of("/#?%",interesting+1); |
230 | const char *nptr = uri.data()+interesting+1; | 235 | const char *nptr = uri.data()+interesting+1; |
231 | char *eptr = 0; | 236 | char *eptr = 0; |
232 | long port = strtol(nptr,&eptr,10); | 237 | long port = strtol(nptr,&eptr,10); |
233 | if( (port>0) && (port<65535) && port!=(s?443:80) ) { | 238 | if( (port>0) && (port<65535) && port!=(s?443:80) ) { |
234 | char tmp[8]; | 239 | char tmp[8]; |
235 | snprintf(tmp,sizeof(tmp),":%ld",port); | 240 | snprintf(tmp,sizeof(tmp),":%ld",port); |
236 | rv += tmp; | 241 | rv += tmp; |
237 | } | 242 | } |
238 | if(ni==string::npos) { | 243 | if(ni==string::npos) { |
239 | rv += '/'; return rv; | 244 | rv += '/'; return rv; |
240 | } | 245 | } |
241 | interesting = ni; | 246 | interesting = ni; |
242 | }else if(ic!='/') { | 247 | }else if(ic!='/') { |
243 | rv += '/'; rv += ic; | 248 | rv += '/'; rv += ic; |
244 | qf = true; | 249 | qf = true; |
245 | ++interesting; | 250 | ++interesting; |
246 | } | 251 | } |
247 | string::size_type n = interesting; | 252 | string::size_type n = interesting; |
248 | char tmp[3] = { 0,0,0 }; | 253 | char tmp[3] = { 0,0,0 }; |
249 | stack<string::size_type> psegs; psegs.push(rv.length()); | 254 | stack<string::size_type> psegs; psegs.push(rv.length()); |
250 | string pseg; | 255 | string pseg; |
251 | for(;n<ul;) { | 256 | for(;n<ul;) { |
252 | string::size_type unsafe = uri.find_first_of(qf?"%":"%/?#",n); | 257 | string::size_type unsafe = uri.find_first_of(qf?"%":"%/?#",n); |
253 | if(unsafe==string::npos) { | 258 | if(unsafe==string::npos) { |
254 | pseg.append(uri,n,ul-n-1); n = ul-1; | 259 | pseg.append(uri,n,ul-n-1); n = ul-1; |
255 | }else{ | 260 | }else{ |
256 | pseg.append(uri,n,unsafe-n); | 261 | pseg.append(uri,n,unsafe-n); |
257 | n = unsafe; | 262 | n = unsafe; |
258 | } | 263 | } |
259 | char c = uri[n++]; | 264 | char c = uri[n++]; |
260 | if(c=='%') { | 265 | if(c=='%') { |
261 | if((n+1)>=ul) | 266 | if((n+1)>=ul) |
262 | throw bad_input(OPKELE_CP_ "Unexpected end of URI encountered while parsing percent-encoded character"); | 267 | throw bad_input(OPKELE_CP_ "Unexpected end of URI encountered while parsing percent-encoded character"); |
263 | tmp[0] = uri[n++]; | 268 | tmp[0] = uri[n++]; |
264 | tmp[1] = uri[n++]; | 269 | tmp[1] = uri[n++]; |
265 | if(!( isxdigit(tmp[0]) && isxdigit(tmp[1]) )) | 270 | if(!( isxdigit(tmp[0]) && isxdigit(tmp[1]) )) |
266 | throw bad_input(OPKELE_CP_ "Invalid percent-encoded character in URI being normalized"); | 271 | throw bad_input(OPKELE_CP_ "Invalid percent-encoded character in URI being normalized"); |
267 | int cc = strtol(tmp,0,16); | 272 | int cc = strtol(tmp,0,16); |
268 | if( isalpha(cc) || isdigit(cc) || strchr("._~-",cc) ) | 273 | if( isalpha(cc) || isdigit(cc) || strchr("._~-",cc) ) |
269 | pseg += cc; | 274 | pseg += cc; |
270 | else{ | 275 | else{ |
271 | pseg += '%'; | 276 | pseg += '%'; |
272 | pseg += toupper(tmp[0]); pseg += toupper(tmp[1]); | 277 | pseg += toupper(tmp[0]); pseg += toupper(tmp[1]); |
273 | } | 278 | } |
274 | }else if(qf) { | 279 | }else if(qf) { |
275 | rv += pseg; rv += c; | 280 | rv += pseg; rv += c; |
276 | pseg.clear(); | 281 | pseg.clear(); |
277 | }else if(n>=ul || strchr("?/#",c)) { | 282 | }else if(n>=ul || strchr("?/#",c)) { |
278 | if(pseg.empty() || pseg==".") { | 283 | if(pseg.empty() || pseg==".") { |
279 | }else if(pseg=="..") { | 284 | }else if(pseg=="..") { |
280 | if(psegs.size()>1) { | 285 | if(psegs.size()>1) { |
281 | rv.resize(psegs.top()); psegs.pop(); | 286 | rv.resize(psegs.top()); psegs.pop(); |
282 | } | 287 | } |
283 | }else{ | 288 | }else{ |
284 | psegs.push(rv.length()); | 289 | psegs.push(rv.length()); |
285 | if(c!='/') { | 290 | if(c!='/') { |
286 | pseg += c; | 291 | pseg += c; |
287 | qf = true; | 292 | qf = true; |
288 | } | 293 | } |
289 | rv += '/'; rv += pseg; | 294 | rv += '/'; rv += pseg; |
290 | } | 295 | } |
291 | if(c=='/' && (n>=ul || strchr("?#",uri[n])) ) { | 296 | if(c=='/' && (n>=ul || strchr("?#",uri[n])) ) { |
292 | rv += '/'; | 297 | rv += '/'; |
293 | if(n<ul) | 298 | if(n<ul) |
294 | qf = true; | 299 | qf = true; |
295 | }else if(strchr("?#",c)) { | 300 | }else if(strchr("?#",c)) { |
296 | if(psegs.size()==1 && psegs.top()==rv.length()) | 301 | if(psegs.size()==1 && psegs.top()==rv.length()) |
297 | rv += '/'; | 302 | rv += '/'; |
298 | if(pseg.empty()) | 303 | if(pseg.empty()) |
299 | rv += c; | 304 | rv += c; |
300 | qf = true; | 305 | qf = true; |
301 | } | 306 | } |
302 | pseg.clear(); | 307 | pseg.clear(); |
303 | }else{ | 308 | }else{ |
304 | pseg += c; | 309 | pseg += c; |
305 | } | 310 | } |
306 | } | 311 | } |
307 | if(!pseg.empty()) { | 312 | if(!pseg.empty()) { |
308 | if(!qf) rv += '/'; | 313 | if(!qf) rv += '/'; |
309 | rv += pseg; | 314 | rv += pseg; |
310 | } | 315 | } |
311 | return rv; | 316 | return rv; |
312 | } | 317 | } |
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 | } |
315 | 382 | ||
316 | } | 383 | } |