summaryrefslogtreecommitdiffabout
path: root/lib
Unidiff
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
@@ -145,25 +145,25 @@ namespace opkele {
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())
@@ -213,25 +213,25 @@ namespace opkele {
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 }
diff --git a/lib/discovery.cc b/lib/discovery.cc
index d868308..93409f4 100644
--- a/lib/discovery.cc
+++ b/lib/discovery.cc
@@ -9,28 +9,45 @@
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
@@ -39,25 +56,25 @@ namespace opkele {
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;
@@ -75,93 +92,132 @@ namespace opkele {
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)
@@ -190,35 +246,39 @@ namespace opkele {
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;
@@ -301,25 +361,26 @@ namespace opkele {
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")) {
@@ -427,20 +488,50 @@ namespace opkele {
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
@@ -100,25 +100,25 @@ namespace opkele {
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 }
diff --git a/lib/sreg.cc b/lib/sreg.cc
index 03edf57..7e2d588 100644
--- a/lib/sreg.cc
+++ b/lib/sreg.cc
@@ -19,49 +19,60 @@ namespace opkele {
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;
@@ -85,42 +96,45 @@ namespace opkele {
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,25 +1,31 @@
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());
@@ -196,26 +202,25 @@ namespace opkele {
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;
@@ -302,15 +307,77 @@ namespace opkele {
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}