summaryrefslogtreecommitdiffabout
path: root/lib
authorMichael Krelin <hacker@klever.net>2008-01-20 21:08:05 (UTC)
committer Michael Krelin <hacker@klever.net>2008-01-20 21:08:05 (UTC)
commit9bfb6fadf71c46bf4cb5adabba0c96c32e84c1bc (patch) (unidiff)
tree702473142242e80538c4801cc379ec98fba199dd /lib
parent395a126cbf59b7a50f44da3096b68bab412ab33d (diff)
downloadlibopkele-9bfb6fadf71c46bf4cb5adabba0c96c32e84c1bc.zip
libopkele-9bfb6fadf71c46bf4cb5adabba0c96c32e84c1bc.tar.gz
libopkele-9bfb6fadf71c46bf4cb5adabba0c96c32e84c1bc.tar.bz2
the whole library rewritten
Signed-off-by: Michael Krelin <hacker@klever.net>
Diffstat (limited to 'lib') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/Makefile.am7
-rw-r--r--lib/basic_rp.cc311
-rw-r--r--lib/consumer.cc4
-rw-r--r--lib/discovery.cc161
-rw-r--r--lib/extension.cc6
-rw-r--r--lib/extension_chain.cc12
-rw-r--r--lib/openid_message.cc228
-rw-r--r--lib/params.cc101
-rw-r--r--lib/prequeue_rp.cc81
-rw-r--r--lib/server.cc2
-rw-r--r--lib/sreg.cc54
-rw-r--r--lib/util.cc71
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 @@
1lib_LTLIBRARIES = libopkele.la 1lib_LTLIBRARIES = libopkele.la
2 2
3AM_CPPFLAGS = ${CPPFLAGS_DEBUG} 3AM_CPPFLAGS = ${CPPFLAGS_DEBUG}
4DEFAULT_INCLUDES = -I${top_builddir} 4DEFAULT_INCLUDES = -I${top_builddir}
5INCLUDES = \ 5INCLUDES = \
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}
11libopkele_la_LIBADD = \ 11libopkele_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
17libopkele_la_SOURCES = \ 17libopkele_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
30libopkele_la_LDFLAGS = \ 33libopkele_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
10namespace 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
17namespace opkele { 17namespace 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
16namespace opkele { 16namespace 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
4namespace opkele { 4namespace 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
4namespace opkele { 4namespace 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
9namespace 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
9namespace opkele { 9namespace 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
13namespace 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
10namespace opkele { 10namespace 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
6namespace opkele { 6namespace opkele {
7 using std::find; 7 using std::find;
8 8
9 static const struct _sreg_field { 9 static const struct _sreg_field {
10 const char *fieldname; 10 const char *fieldname;
11 sreg_t::fieldbit_t fieldbit; 11 sreg_t::fieldbit_t fieldbit;
12 }fields[] = { 12 }fields[] = {
13 { "nickname", sreg_t::field_nickname }, 13 { "nickname", sreg_t::field_nickname },
14 { "email", sreg_t::field_email }, 14 { "email", sreg_t::field_email },
15 { "fullname", sreg_t::field_fullname }, 15 { "fullname", sreg_t::field_fullname },
16 { "dob", sreg_t::field_dob }, 16 { "dob", sreg_t::field_dob },
17 { "gender", sreg_t::field_gender }, 17 { "gender", sreg_t::field_gender },
18 { "postcode", sreg_t::field_postcode }, 18 { "postcode", sreg_t::field_postcode },
19 { "country", sreg_t::field_country }, 19 { "country", sreg_t::field_country },
20 { "language", sreg_t::field_language }, 20 { "language", sreg_t::field_language },
21 { "timezone", sreg_t::field_timezone } 21 { "timezone", sreg_t::field_timezone }
22 }; 22 };
23 # define fields_BEGINfields 23 # define fields_BEGINfields
24# define fields_END &fields[sizeof(fields)/sizeof(*fields)] 24# define fields_END &fields[sizeof(fields)/sizeof(*fields)]
25 typedef const struct _sreg_field *fields_iterator; 25 typedef const struct _sreg_field *fields_iterator;
26 26
27 bool operator==(const struct _sreg_field& fd,const string& fn) { 27 bool operator==(const struct _sreg_field& fd,const string& fn) {
28 return fd.fieldname==fn; 28 return fd.fieldname==fn;
29 } 29 }
30 30
31 void sreg_t::checkid_hook(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
14namespace opkele { 20namespace 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}