summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--include/Makefile.am21
-rw-r--r--include/opkele/basic_rp.h218
-rw-r--r--include/opkele/discovery.h88
-rw-r--r--include/opkele/exception.h19
-rw-r--r--include/opkele/extension.h7
-rw-r--r--include/opkele/extension_chain.h6
-rw-r--r--include/opkele/prequeue_rp.h81
-rw-r--r--include/opkele/sreg.h8
-rw-r--r--include/opkele/types.h169
-rw-r--r--include/opkele/util.h8
-rw-r--r--lib/Makefile.am7
-rw-r--r--lib/basic_rp.cc311
-rw-r--r--lib/consumer.cc4
-rw-r--r--lib/discovery.cc155
-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
22 files changed, 1362 insertions, 295 deletions
diff --git a/include/Makefile.am b/include/Makefile.am
index 51dcea1..50fcb62 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,5 +1,8 @@
1nobase_include_HEADERS = \ 1NODIST_HEADERS_ = \
2 opkele/acconfig.h \ 2 opkele/acconfig.h \
3 opkele/tr1-mem.h
4
5nobase_include_HEADERS = \
3 opkele/opkele-config.h \ 6 opkele/opkele-config.h \
4 opkele/types.h \ 7 opkele/types.h \
5 opkele/association.h \ 8 opkele/association.h \
@@ -11,11 +14,19 @@ nobase_include_HEADERS = \
11 opkele/extension_chain.h \ 14 opkele/extension_chain.h \
12 opkele/xconsumer.h \ 15 opkele/xconsumer.h \
13 opkele/xserver.h \ 16 opkele/xserver.h \
14 opkele/discovery.h \
15 opkele/uris.h \ 17 opkele/uris.h \
16 opkele/tr1-mem.h 18 opkele/tr1-mem.h \
17EXTRA_DIST = \ 19 opkele/basic_rp.h \
20 opkele/prequeue_rp.h \
21 opkele/iterator.h \
22 ${NODIST_HEADERS_}
23
24noinst_HEADERS = \
18 opkele/data.h \ 25 opkele/data.h \
19 opkele/curl.h opkele/expat.h opkele/tidy.h \ 26 opkele/curl.h opkele/expat.h opkele/tidy.h \
20 opkele/util.h \ 27 opkele/util.h \
21 opkele/debug.h 28 opkele/debug.h \
29 opkele/discovery.h
30
31dist-hook:
32 rm -f $(addprefix ${distdir}/,${NODIST_HEADERS_})
diff --git a/include/opkele/basic_rp.h b/include/opkele/basic_rp.h
new file mode 100644
index 0000000..3f17fd9
--- a/dev/null
+++ b/include/opkele/basic_rp.h
@@ -0,0 +1,218 @@
1#ifndef __OPKELE_BASIC_RP_H
2#define __OPKELE_BASIC_RP_H
3
4#include <cstring>
5#include <string>
6#include <opkele/types.h>
7#include <opkele/extension.h>
8
9namespace opkele {
10 using std::string;
11
12 struct openid_endpoint_t {
13 string uri;
14 string claimed_id;
15 string local_id;
16
17 openid_endpoint_t() { }
18 openid_endpoint_t(const string& u,const string& cid,const string& lid)
19 : uri(u), claimed_id(cid), local_id(lid) { }
20
21 bool operator==(const openid_endpoint_t& x) const {
22 return uri==x.uri && local_id==x.local_id; }
23 bool operator<(const openid_endpoint_t& x) const {
24 int c;
25 return (c=strcmp(uri.c_str(),x.uri.c_str()))
26 ? (c<0) : (strcmp(local_id.c_str(),x.local_id.c_str())<0); }
27 };
28
29 class basic_RP {
30 public:
31
32 virtual ~basic_RP() { }
33
34 /**
35 * @name Global persistent store API
36 * These are functions related to the associations with OP storage
37 * and retrieval and nonce records. They provide an interface to
38 * the persistent storage which is shared by all sessions. If the
39 * implementor prefers the dumb mode instead, the function should
40 * throw dumb_RP exception instead.
41 * @see opkele::dumb_RP
42 * @{
43 */
44 /**
45 * Store association and return allocated association object.
46 * @param OP OP endpoint
47 * @param handle association handle
48 * @param type association type
49 * @param secret association secret
50 * @params expires_in the number of seconds association expires in
51 * @return the association object
52 * @throw dumb_RP for dumb RP
53 */
54 virtual assoc_t store_assoc(
55 const string& OP,const string& handle,
56 const string& type,const secret_t& secret,
57 int expires_in) = 0;
58 /**
59 * Find valid unexpired association with an OP.
60 * @param OP OP endpoint URL
61 * @return association found
62 * @throw failed_lookup if no association found
63 * @throw dumb_RP for dumb RP
64 */
65 virtual assoc_t find_assoc(
66 const string& OP) = 0;
67 /**
68 * Retrieve valid association handle for an OP by handle.
69 * @param OP OP endpoint URL
70 * @param handle association handle
71 * @return association found
72 * @throw failed_lookup if no association found
73 * @throw dumb_RP for dumb RP
74 */
75 virtual assoc_t retrieve_assoc(
76 const string& OP,const string& handle) = 0;
77 /**
78 * Invalidate association with OP
79 * @param OP OP endpoint URL
80 * @param handle association handle
81 * @throw dumb_RP for dumb RP
82 */
83 virtual void invalidate_assoc(const string& OP,const string& handle) = 0;
84
85 /**
86 * Check the nonce validity. That is, check that we haven't
87 * accepted request with this nonce from this OP, yet. May involve
88 * cutting off by the timestamp and checking the rest against the
89 * store of seen nonces.
90 * @param OP OP endpoint URL
91 * @param nonce nonce value
92 * @throw id_res_bad_nonce if the nonce is not to be accepted, i.e.
93 * either too old or seen.
94 */
95 virtual void check_nonce(const string& OP,const string& nonce) = 0;
96 /**
97 * @}
98 */
99
100 /**
101 * @name Session persistent store API
102 * @{
103 */
104 /**
105 * Retrieve OpenID endpoint being currently used for
106 * authentication. If there is no endpoint available, throw a
107 * no_endpoint exception.
108 * @return reference to the service endpoint object
109 * @see next_endpoint
110 * @throw no_endpoint if no endpoint available
111 */
112 virtual const openid_endpoint_t& get_endpoint() const = 0;
113 /**
114 * Advance to the next endpoint to try.
115 * @see get_endpoint()
116 * @throw no_endpoint if there are no more endpoints
117 */
118 virtual void next_endpoint() = 0;
119 /**
120 * @}
121 */
122
123 /**
124 * @name Site particulars API
125 * @{
126 */
127 /**
128 * Return an absolute URL of the page being processed, includining
129 * query parameters. It is used to validate return_to URL on
130 * positive assertions.
131 * @return fully qualified url of the page being processed.
132 */
133 virtual const string get_this_url() const = 0;
134 /**
135 * @}
136 */
137
138 /**
139 * @name OpenID actions
140 * @{
141 */
142 /**
143 * Initiates authentication session, doing discovery, normalization
144 * and whatever implementor wants to do at this point.
145 * @param usi User-supplied identity
146 */
147 virtual void initiate(const string& usi) = 0;
148 /**
149 * Prepare checkid_request.
150 * @param rv reference to the openid message to prepare
151 * @param mode checkid_setup or checkid_immediate
152 * @param return_to the URL OP should redirect to after completion
153 * @param realm authentication realm to pass to OP
154 * @param ext pointer to extension to use in request preparation
155 * @return reference to the openid message
156 */
157 basic_openid_message& checkid_(
158 basic_openid_message& rv,
159 mode_t mode,
160 const string& return_to,const string& realm,
161 extension_t *ext=0);
162 /**
163 * Verify assertion at the end of round-trip.
164 * @param om incoming openid message
165 * @param ext pointer to extention to use in parsing assertion
166 * @throw id_res_setup if checkid_immediate request could not be
167 * completed
168 * @throw id_res_cancel if authentication request was canceled
169 * @throw id_res_mismatch in case of signature mismatch
170 * @throw id_res_bad_return_to if return_to url seems to be
171 * tampered with
172 * @throw id_res_unauthorized if OP is not authorized to make
173 * assertions regarding the identity
174 */
175 void id_res(const basic_openid_message& om,extension_t *ext=0);
176
177 /**
178 * Establish association with OP
179 * @param OP OP to establish association with
180 * @throw dumb_RP if for a dumb RP
181 */
182 virtual assoc_t associate(const string& OP);
183 /**
184 * Check authentication with OP and invalidate handle if requested
185 * and confirmed
186 * @param OP OP to check with
187 * @param om message to check
188 * @throw failed_check_authentication if OP fails to confirm
189 * authenticity of the assertion
190 */
191 void check_authentication(const string& OP,const basic_openid_message& om);
192 /**
193 * @}
194 */
195
196 /**
197 * @name Miscellanea
198 * @{
199 */
200 /**
201 * Verify OP authority. Return normally if OP is authorized to make
202 * an assertion, throw an exception otherwise.
203 * @param OP OP endpoint
204 * @param claimed_id claimed identity
205 * @param identity OP-Local identifier
206 * @throw id_res_unauthorized if OP is not authorized to make
207 * assertion regarding this identity.
208 */
209 virtual void verify_OP(const string& OP,
210 const string& claimed_id,const string& identity) const = 0;
211 /**
212 * @}
213 */
214 };
215
216}
217
218#endif /* __OPKELE_BASIC_RP_H */
diff --git a/include/opkele/discovery.h b/include/opkele/discovery.h
index af4aa29..ab4b9d9 100644
--- a/include/opkele/discovery.h
+++ b/include/opkele/discovery.h
@@ -3,13 +3,87 @@
3 3
4#include <string> 4#include <string>
5#include <opkele/types.h> 5#include <opkele/types.h>
6#include <opkele/basic_rp.h>
6 7
7namespace opkele { 8namespace opkele {
8 using std::string; 9 using std::string;
9 10
10 struct idiscovery_t; 11 namespace xrd {
11 12
12 void idiscover(idiscovery_t& result,const string& identity); 13 struct priority_compare {
14 inline bool operator()(long a,long b) const {
15 return (a<0) ? false : (b<0) ? true : (a<b);
16 }
17 };
18
19 template <typename _DT>
20 class priority_map : public multimap<long,_DT,priority_compare> {
21 typedef multimap<long,_DT,priority_compare> map_type;
22 public:
23
24 inline _DT& add(long priority,const _DT& d) {
25 return insert(typename map_type::value_type(priority,d))->second;
26 }
27
28 bool has_value(const _DT& d) const {
29 for(typename map_type::const_iterator i=this->begin();i!=this->end();++i)
30 if(i->second==d) return true;
31 return false;
32 }
33 };
34
35 typedef priority_map<string> canonical_ids_t;
36 typedef priority_map<string> local_ids_t;
37 typedef set<string> types_t;
38 typedef priority_map<string> uris_t;
39
40 class service_t {
41 public:
42 types_t types;
43 uris_t uris;
44 local_ids_t local_ids;
45 string provider_id;
46
47 void clear() {
48 types.clear();
49 uris.clear(); local_ids.clear();
50 provider_id.clear();
51 }
52 };
53 typedef priority_map<service_t> services_t;
54
55 class XRD_t {
56 public:
57 time_t expires;
58
59 canonical_ids_t canonical_ids;
60 local_ids_t local_ids;
61 services_t services;
62 string provider_id;
63
64 void clear() {
65 expires = 0;
66 canonical_ids.clear(); local_ids.clear();
67 services.clear();
68 provider_id.clear();
69 }
70 bool empty() const {
71 return
72 canonical_ids.empty()
73 && local_ids.empty()
74 && services.empty();
75 }
76
77 };
78
79 }
80
81 typedef util::output_iterator_proxy<openid_endpoint_t>
82 endpoint_discovery_iterator;
83
84 string idiscover(
85 endpoint_discovery_iterator oi,
86 const string& identity);
13 87
14 struct idiscovery_t { 88 struct idiscovery_t {
15 bool xri_identity; 89 bool xri_identity;
@@ -18,22 +92,12 @@ namespace opkele {
18 xrd::XRD_t xrd; 92 xrd::XRD_t xrd;
19 93
20 idiscovery_t() { } 94 idiscovery_t() { }
21 idiscovery_t(const string& i) {
22 idiscover(*this,i);
23 }
24 idiscovery_t(const char *i) {
25 idiscover(*this,i);
26 }
27 95
28 void clear() { 96 void clear() {
29 normalized_id.clear(); canonicalized_id.clear(); 97 normalized_id.clear(); canonicalized_id.clear();
30 xrd.clear(); 98 xrd.clear();
31 } 99 }
32 100
33 idiscovery_t& operator=(const string& i) {
34 idiscover(*this,i); return *this; }
35 idiscovery_t& operator=(const char *i) {
36 idiscover(*this,i); return *this; }
37 }; 101 };
38} 102}
39 103
diff --git a/include/opkele/exception.h b/include/opkele/exception.h
index a8c3339..ccb39d9 100644
--- a/include/opkele/exception.h
+++ b/include/opkele/exception.h
@@ -311,6 +311,25 @@ namespace opkele {
311 : exception(OPKELE_E_CONS) { } 311 : exception(OPKELE_E_CONS) { }
312 }; 312 };
313 313
314 /**
315 * thrown by associations store related functions in case of dumb RP.
316 */
317 class dumb_RP : public exception {
318 public:
319 dumb_RP(OPKELE_E_PARS)
320 : exception(OPKELE_E_CONS) { }
321 };
322
323 /**
324 * thrown by endpoint-queue related function if endpoint is being
325 * accessed but there's no endpoint available.
326 */
327 class no_endpoint : public exception {
328 public:
329 no_endpoint(OPKELE_E_PARS)
330 : exception(OPKELE_E_CONS) { }
331 };
332
314} 333}
315 334
316#endif /* __OPKELE_EXCEPTION_H */ 335#endif /* __OPKELE_EXCEPTION_H */
diff --git a/include/opkele/extension.h b/include/opkele/extension.h
index 513672f..3ee25ee 100644
--- a/include/opkele/extension.h
+++ b/include/opkele/extension.h
@@ -17,6 +17,7 @@ namespace opkele {
17 public: 17 public:
18 18
19 virtual ~extension_t() { } 19 virtual ~extension_t() { }
20
20 /** 21 /**
21 * hook called by consumer before submitting data to OpenID server. 22 * hook called by consumer before submitting data to OpenID server.
22 * It is supposed to manipulate parameters list. 23 * It is supposed to manipulate parameters list.
@@ -27,7 +28,7 @@ namespace opkele {
27 * @see consumer_t::checkid_immediate 28 * @see consumer_t::checkid_immediate
28 * @see consumer_t::checkid_setup 29 * @see consumer_t::checkid_setup
29 */ 30 */
30 virtual void checkid_hook(params_t& p,const string& identity); 31 virtual void checkid_hook(basic_openid_message& om);
31 /** 32 /**
32 * hook called by consumer after identity information received from 33 * hook called by consumer after identity information received from
33 * OpenID server is verified. 34 * OpenID server is verified.
@@ -39,7 +40,7 @@ namespace opkele {
39 * empty which means - extract one from parameters 40 * empty which means - extract one from parameters
40 * @see consumer_t::id_res 41 * @see consumer_t::id_res
41 */ 42 */
42 virtual void id_res_hook(const params_t& p,const params_t& sp,const string& identity); 43 virtual void id_res_hook(const basic_openid_message& om,const basic_openid_message& sp);
43 44
44 /** 45 /**
45 * hook called by server before returning information to consumer. 46 * hook called by server before returning information to consumer.
@@ -51,7 +52,7 @@ namespace opkele {
51 * @see server_t::checkid_immediate 52 * @see server_t::checkid_immediate
52 * @see server_t::checkid_setup 53 * @see server_t::checkid_setup
53 */ 54 */
54 virtual void checkid_hook(const params_t& pin,params_t& pout); 55 virtual void checkid_hook(const basic_openid_message& inm,basic_openid_message& oum);
55 56
56 /** 57 /**
57 * Casts the object to pointer to itself. For convenient passing 58 * Casts the object to pointer to itself. For convenient passing
diff --git a/include/opkele/extension_chain.h b/include/opkele/extension_chain.h
index f0eea94..fb9bc84 100644
--- a/include/opkele/extension_chain.h
+++ b/include/opkele/extension_chain.h
@@ -28,9 +28,9 @@ namespace opkele {
28 */ 28 */
29 extension_chain_t(extension_t *e) { push_back(e); } 29 extension_chain_t(extension_t *e) { push_back(e); }
30 30
31 virtual void checkid_hook(params_t& p,const string& identity); 31 virtual void checkid_hook(basic_openid_message& om);
32 virtual void id_res_hook(const params_t& p,const params_t& sp,const string& identity); 32 virtual void id_res_hook(const basic_openid_message& om,const basic_openid_message& sp);
33 virtual void checkid_hook(const params_t& pin,params_t& pout); 33 virtual void checkid_hook(const basic_openid_message& inm,basic_openid_message& oum);
34 }; 34 };
35 35
36} 36}
diff --git a/include/opkele/prequeue_rp.h b/include/opkele/prequeue_rp.h
new file mode 100644
index 0000000..b98dd5a
--- a/dev/null
+++ b/include/opkele/prequeue_rp.h
@@ -0,0 +1,81 @@
1#ifndef __OPKELE_RP_H
2#define __OPKELE_RP_H
3
4#include <string>
5#include <set>
6#include <iterator>
7#include <opkele/basic_rp.h>
8
9namespace opkele {
10 using std::string;
11 using std::set;
12 using std::iterator;
13 using std::output_iterator_tag;
14
15 class prequeue_RP : public basic_RP {
16 public:
17 /**
18 * @name Session persistent store API
19 * @{
20 */
21 /**
22 * Called before queueing discovered endpoints. Typically happens
23 * while initiating authentication session.
24 * @see queue_endpoint()
25 * @see end_queueing()
26 */
27 virtual void begin_queueing() { }
28 /**
29 * Used to queue discovered endpoint. It is implementors
30 * responsibility to store the endpoint wherever he choses to store
31 * it.
32 * @param oep the endpoint to queue
33 * @see begin_queueing()
34 * @see end_queueing()
35 */
36 virtual void queue_endpoint(const openid_endpoint_t& oep) = 0;
37 /**
38 * Called after all discovered endpoints were queued. Implementor
39 * may chose to use this virtual to commit endpoints queue to
40 * persistent store.
41 * @see begin_queueing()
42 * @see queue_endpoint()
43 */
44 virtual void end_queueing() { }
45
46 /**
47 * Used to store normalized id when initiating request.
48 * The default implementation does nothing, because implementor
49 * doesn't have to care.
50 * @param nid normalized id
51 * @see get_normalzied_id()
52 */
53 virtual void set_normalized_id(const string& nid);
54 /**
55 * Return the normalized id previously set by set_normalized_id().
56 * Provided for the sake of completeness because default
57 * implementation doesn't use it.
58 * @return the normalized identity
59 */
60 virtual const string get_normalized_id() const;
61 /**
62 * @}
63 */
64
65 /**
66 * @name Actions
67 * @{
68 */
69 void initiate(const string& usi);
70
71 /**
72 * @}
73 */
74
75 void verify_OP(const string& OP,
76 const string& claimed_id,const string& identity) const;
77 };
78
79}
80
81#endif /* __OPKELE_RP_H */
diff --git a/include/opkele/sreg.h b/include/opkele/sreg.h
index df37a86..24cb315 100644
--- a/include/opkele/sreg.h
+++ b/include/opkele/sreg.h
@@ -138,15 +138,15 @@ namespace opkele {
138 /** 138 /**
139 * Implementation of consumer's checkid hook 139 * Implementation of consumer's checkid hook
140 */ 140 */
141 virtual void checkid_hook(params_t& p,const string& identity); 141 virtual void checkid_hook(basic_openid_message& om);
142 /** 142 /**
143 * Implementation of consumer's id_res hook 143 * Implementation of consumer's id_res hook
144 */ 144 */
145 virtual void id_res_hook(const params_t& p,const params_t& sp,const string& identity); 145 virtual void id_res_hook(const basic_openid_message& om,const basic_openid_message& sp);
146 /** 146 /**
147 * Implementation of server's checkid_hook 147 * Implementation of server's checkid_hook
148 */ 148 */
149 virtual void checkid_hook(const params_t& pin,params_t& pout); 149 virtual void checkid_hook(const basic_openid_message& inm,basic_openid_message& oum);
150 150
151 /** 151 /**
152 * Check and see if we have value for some particular field. 152 * Check and see if we have value for some particular field.
@@ -195,7 +195,7 @@ namespace opkele {
195 * @param pout output request parameters without "openid." prefix. 195 * @param pout output request parameters without "openid." prefix.
196 * @see checkid_hook(const params_t&,params_t&) 196 * @see checkid_hook(const params_t&,params_t&)
197 */ 197 */
198 virtual void setup_response(const params_t& pin,params_t& pout); 198 virtual void setup_response(const basic_openid_message& inm,basic_openid_message& oum);
199 199
200 }; 200 };
201} 201}
diff --git a/include/opkele/types.h b/include/opkele/types.h
index de44a5c..d5ad258 100644
--- a/include/opkele/types.h
+++ b/include/opkele/types.h
@@ -11,6 +11,8 @@
11#include <string> 11#include <string>
12#include <map> 12#include <map>
13#include <set> 13#include <set>
14#include <list>
15#include <opkele/iterator.h>
14#include <opkele/tr1-mem.h> 16#include <opkele/tr1-mem.h>
15 17
16namespace opkele { 18namespace opkele {
@@ -20,6 +22,9 @@ namespace opkele {
20 using std::ostream; 22 using std::ostream;
21 using std::multimap; 23 using std::multimap;
22 using std::set; 24 using std::set;
25 using std::list;
26 using std::iterator;
27 using std::forward_iterator_tag;
23 28
24 /** 29 /**
25 * the OpenID operation mode 30 * the OpenID operation mode
@@ -111,10 +116,63 @@ namespace opkele {
111 */ 116 */
112 typedef tr1mem::shared_ptr<association_t> assoc_t; 117 typedef tr1mem::shared_ptr<association_t> assoc_t;
113 118
119 class basic_openid_message {
120 public:
121 typedef list<string> fields_t;
122 typedef util::forward_iterator_proxy<
123 string,const string&,const string*
124 > fields_iterator;
125
126 basic_openid_message() { }
127 basic_openid_message(const basic_openid_message& x);
128 void copy_to(basic_openid_message& x) const;
129
130 virtual bool has_field(const string& n) const = 0;
131 virtual const string& get_field(const string& n) const = 0;
132
133 virtual bool has_ns(const string& uri) const;
134 virtual string get_ns(const string& uri) const;
135
136 virtual fields_iterator fields_begin() const = 0;
137 virtual fields_iterator fields_end() const = 0;
138
139 virtual string append_query(const string& url) const;
140 virtual string query_string() const;
141
142
143 virtual void reset_fields();
144 virtual void set_field(const string& n,const string& v);
145 virtual void reset_field(const string& n);
146
147 virtual void from_keyvalues(const string& kv);
148
149 void add_to_signed(const string& fields);
150 string find_ns(const string& uri,const char *pfx) const;
151 string allocate_ns(const string& uri,const char *pfx);
152 };
153
154 class openid_message_t : public basic_openid_message, public map<string,string> {
155 public:
156 openid_message_t() { }
157 openid_message_t(const basic_openid_message& x)
158 : basic_openid_message(x) { }
159
160 void copy_to(basic_openid_message& x) const;
161
162 bool has_field(const string& n) const;
163 const string& get_field(const string& n) const;
164 virtual fields_iterator fields_begin() const;
165 virtual fields_iterator fields_end() const;
166
167 void reset_fields();
168 void set_field(const string& n,const string& v);
169 void reset_field(const string& n);
170 };
171
114 /** 172 /**
115 * request/response parameters map 173 * request/response parameters map
116 */ 174 */
117 class params_t : public map<string,string> { 175 class params_t : public openid_message_t {
118 public: 176 public:
119 177
120 /** 178 /**
@@ -122,125 +180,28 @@ namespace opkele {
122 * @param n the parameter name 180 * @param n the parameter name
123 * @return true if yes 181 * @return true if yes
124 */ 182 */
125 bool has_param(const string& n) const; 183 bool has_param(const string& n) const {
184 return has_field(n); }
126 /** 185 /**
127 * retrieve the parameter (const version) 186 * retrieve the parameter (const version)
128 * @param n the parameter name 187 * @param n the parameter name
129 * @return the parameter value 188 * @return the parameter value
130 * @throw failed_lookup if there is no such parameter 189 * @throw failed_lookup if there is no such parameter
131 */ 190 */
132 const string& get_param(const string& n) const; 191 const string& get_param(const string& n) const {
133 /** 192 return get_field(n); }
134 * retrieve the parameter.
135 * @param n the parameter name
136 * @return the parameter value
137 * @throw failed_lookup if there is no such parameter
138 */
139 string& get_param(const string& n);
140 193
141 /** 194 /**
142 * parse the OpenID key/value data. 195 * parse the OpenID key/value data.
143 * @param kv the OpenID key/value data 196 * @param kv the OpenID key/value data
144 */ 197 */
145 void parse_keyvalues(const string& kv); 198 void parse_keyvalues(const string& kv) {
146 /** 199 from_keyvalues(kv); }
147 * sign the fields.
148 * @param secret the secret used for signing
149 * @param sig reference to the string, containing base64-encoded
150 * result
151 * @param slist the comma-separated list of fields to sign
152 * @param prefix the string to prepend to parameter names
153 */
154 void sign(secret_t secret,string& sig,const string& slist,const char *prefix=0) const;
155 200
156 /**
157 * append parameters to the URL as a GET-request parameters.
158 * @param url the base URL
159 * @param prefix the string to prepend to parameter names
160 * @return the ready-to-use location
161 */
162 string append_query(const string& url,const char *prefix = "openid.") const; 201 string append_query(const string& url,const char *prefix = "openid.") const;
163 202
164 /**
165 * make up a query string suitable for use in GET and POST
166 * requests.
167 * @param prefix string to prened to parameter names
168 * @return query string
169 */
170 string query_string(const char *prefix = "openid.") const;
171 };
172
173 /**
174 * dump the key/value pairs for the parameters to the stream.
175 * @param o output stream
176 * @param p the parameters
177 */
178 ostream& operator << (ostream& o,const params_t& p);
179
180 namespace xrd {
181
182 struct priority_compare {
183 inline bool operator()(long a,long b) const {
184 return (a<0) ? false : (b<0) ? true : (a<b);
185 }
186 };
187
188 template <typename _DT>
189 class priority_map : public multimap<long,_DT,priority_compare> {
190 typedef multimap<long,_DT,priority_compare> map_type;
191 public:
192
193 inline _DT& add(long priority,const _DT& d) {
194 return insert(typename map_type::value_type(priority,d))->second;
195 }
196 }; 203 };
197 204
198 typedef priority_map<string> canonical_ids_t;
199 typedef priority_map<string> local_ids_t;
200 typedef set<string> types_t;
201 typedef priority_map<string> uris_t;
202
203 class service_t {
204 public:
205 types_t types;
206 uris_t uris;
207 local_ids_t local_ids;
208 string provider_id;
209
210 void clear() {
211 types.clear();
212 uris.clear(); local_ids.clear();
213 provider_id.clear();
214 }
215 };
216 typedef priority_map<service_t> services_t;
217
218 class XRD_t {
219 public:
220 time_t expires;
221
222 canonical_ids_t canonical_ids;
223 local_ids_t local_ids;
224 services_t services;
225 string provider_id;
226
227 void clear() {
228 expires = 0;
229 canonical_ids.clear(); local_ids.clear();
230 services.clear();
231 provider_id.clear();
232 }
233 bool empty() const {
234 return
235 canonical_ids.empty()
236 && local_ids.empty()
237 && services.empty();
238 }
239
240 };
241
242 }
243
244} 205}
245 206
246#endif /* __OPKELE_TYPES_H */ 207#endif /* __OPKELE_TYPES_H */
diff --git a/include/opkele/util.h b/include/opkele/util.h
index 085c9e6..e9176b0 100644
--- a/include/opkele/util.h
+++ b/include/opkele/util.h
@@ -6,6 +6,7 @@
6#include <vector> 6#include <vector>
7#include <openssl/bn.h> 7#include <openssl/bn.h>
8#include <openssl/dh.h> 8#include <openssl/dh.h>
9#include <opkele/types.h>
9 10
10namespace opkele { 11namespace opkele {
11 using std::string; 12 using std::string;
@@ -136,6 +137,13 @@ namespace opkele {
136 * @throw bad_input in case of malformed URI 137 * @throw bad_input in case of malformed URI
137 */ 138 */
138 string rfc_3986_normalize_uri(const string& uri); 139 string rfc_3986_normalize_uri(const string& uri);
140
141 string& strip_uri_fragment_part(string& uri);
142
143 string abi_demangle(const char* mn);
144
145 string base64_signature(const assoc_t& assoc,const basic_openid_message& om);
146
139 } 147 }
140 148
141} 149}
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;
124 for(const struct service_type_t *st=service_types;
125 st<&service_types[sizeof(service_types)/sizeof(*service_types)];++st) {
126 idis.clear();
127 discover_at( idis,
128 xri_proxy + util::url_encode(id)+
129 "?_xrd_t="+util::url_encode(st->uri)+
130 "&_xrd_r=application/xrd%2Bxml"
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,
105 xri_proxy + util::url_encode(id)+ 145 xri_proxy + util::url_encode(id)+
106 "?_xrd_r=application/xrd+xml;sep=false", xmode_xrd); 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;
107 if(status_code!=100) 151 if(status_code!=100)
108 throw failed_xri_resolution(OPKELE_CP_ 152 throw failed_xri_resolution(OPKELE_CP_
109 "XRI resolution failed with '"+status_string+"' message",status_code); 153 "XRI resolution failed with '"+status_string+"' message"
110 if(result.xrd.canonical_ids.empty()) 154 ", while looking for SEP with type '"+st->uri+"'"
111 throw opkele::failed_discovery(OPKELE_CP_ "No CanonicalID for XRI identity found"); 155 " on canonical id", status_code);
112 result.canonicalized_id = result.xrd.canonical_ids.begin()->second; 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 += '=';
97 rv += util::url_encode(i->second);
98 }
99 return rv;
100 }
101
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 += '='; 24 rv += '=';
110 rv += util::url_encode(i->second); 25 rv += util::url_encode(get_field(*i));
111 } 26 }
112 return rv; 27 return rv;
113 } 28 }
114 29
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}