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
@@ -3,7 +3,7 @@ lib_LTLIBRARIES = libopkele.la
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} \
@@ -26,6 +26,9 @@ libopkele_la_SOURCES = \
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
@@ -154,7 +154,7 @@ namespace opkele {
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
@@ -222,7 +222,7 @@ namespace opkele {
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) {
diff --git a/lib/discovery.cc b/lib/discovery.cc
index d868308..93409f4 100644
--- a/lib/discovery.cc
+++ b/lib/discovery.cc
@@ -18,10 +18,27 @@ namespace opkele {
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 }
@@ -48,7 +65,7 @@ namespace opkele {
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
@@ -84,11 +101,12 @@ namespace opkele {
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))
@@ -96,22 +114,51 @@ namespace opkele {
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('#');
@@ -122,24 +169,33 @@ namespace opkele {
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);
@@ -152,7 +208,7 @@ namespace opkele {
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)
@@ -199,17 +255,21 @@ namespace opkele {
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
@@ -310,7 +370,8 @@ namespace opkele {
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);
@@ -436,11 +497,41 @@ namespace opkele {
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
@@ -3,13 +3,13 @@
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
@@ -3,14 +3,14 @@
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
@@ -9,113 +9,22 @@
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
@@ -109,7 +109,7 @@ namespace opkele {
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) {
diff --git a/lib/sreg.cc b/lib/sreg.cc
index 03edf57..7e2d588 100644
--- a/lib/sreg.cc
+++ b/lib/sreg.cc
@@ -28,7 +28,7 @@ namespace opkele {
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) {
@@ -40,19 +40,30 @@ namespace opkele {
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
@@ -94,33 +105,36 @@ namespace opkele {
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
@@ -7,10 +7,16 @@
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
@@ -205,8 +211,7 @@ namespace opkele {
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);
@@ -311,6 +316,68 @@ namespace opkele {
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}