author | Michael Krelin <hacker@klever.net> | 2008-01-20 21:08:05 (UTC) |
---|---|---|
committer | Michael Krelin <hacker@klever.net> | 2008-01-20 21:08:05 (UTC) |
commit | 9bfb6fadf71c46bf4cb5adabba0c96c32e84c1bc (patch) (unidiff) | |
tree | 702473142242e80538c4801cc379ec98fba199dd | |
parent | 395a126cbf59b7a50f44da3096b68bab412ab33d (diff) | |
download | libopkele-9bfb6fadf71c46bf4cb5adabba0c96c32e84c1bc.zip libopkele-9bfb6fadf71c46bf4cb5adabba0c96c32e84c1bc.tar.gz libopkele-9bfb6fadf71c46bf4cb5adabba0c96c32e84c1bc.tar.bz2 |
the whole library rewritten
Signed-off-by: Michael Krelin <hacker@klever.net>
-rw-r--r-- | include/Makefile.am | 21 | ||||
-rw-r--r-- | include/opkele/basic_rp.h | 218 | ||||
-rw-r--r-- | include/opkele/discovery.h | 88 | ||||
-rw-r--r-- | include/opkele/exception.h | 19 | ||||
-rw-r--r-- | include/opkele/extension.h | 7 | ||||
-rw-r--r-- | include/opkele/extension_chain.h | 6 | ||||
-rw-r--r-- | include/opkele/prequeue_rp.h | 81 | ||||
-rw-r--r-- | include/opkele/sreg.h | 8 | ||||
-rw-r--r-- | include/opkele/types.h | 171 | ||||
-rw-r--r-- | include/opkele/util.h | 8 | ||||
-rw-r--r-- | lib/Makefile.am | 7 | ||||
-rw-r--r-- | lib/basic_rp.cc | 311 | ||||
-rw-r--r-- | lib/consumer.cc | 4 | ||||
-rw-r--r-- | lib/discovery.cc | 161 | ||||
-rw-r--r-- | lib/extension.cc | 6 | ||||
-rw-r--r-- | lib/extension_chain.cc | 12 | ||||
-rw-r--r-- | lib/openid_message.cc | 228 | ||||
-rw-r--r-- | lib/params.cc | 101 | ||||
-rw-r--r-- | lib/prequeue_rp.cc | 81 | ||||
-rw-r--r-- | lib/server.cc | 2 | ||||
-rw-r--r-- | lib/sreg.cc | 54 | ||||
-rw-r--r-- | lib/util.cc | 71 |
22 files changed, 1366 insertions, 299 deletions
diff --git a/include/Makefile.am b/include/Makefile.am index 51dcea1..50fcb62 100644 --- a/include/Makefile.am +++ b/include/Makefile.am | |||
@@ -1,21 +1,32 @@ | |||
1 | nobase_include_HEADERS = \ | 1 | NODIST_HEADERS_ = \ |
2 | opkele/acconfig.h \ | 2 | opkele/acconfig.h \ |
3 | opkele/tr1-mem.h | ||
4 | |||
5 | nobase_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 \ |
6 | opkele/exception.h \ | 9 | opkele/exception.h \ |
7 | opkele/server.h \ | 10 | opkele/server.h \ |
8 | opkele/consumer.h \ | 11 | opkele/consumer.h \ |
9 | opkele/extension.h \ | 12 | opkele/extension.h \ |
10 | opkele/sreg.h \ | 13 | opkele/sreg.h \ |
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 \ |
17 | EXTRA_DIST = \ | 19 | opkele/basic_rp.h \ |
20 | opkele/prequeue_rp.h \ | ||
21 | opkele/iterator.h \ | ||
22 | ${NODIST_HEADERS_} | ||
23 | |||
24 | noinst_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 | |||
31 | dist-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 | |||
9 | namespace 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 | |||
@@ -1,40 +1,104 @@ | |||
1 | #ifndef __OPKELE_DISCOVERY_H | 1 | #ifndef __OPKELE_DISCOVERY_H |
2 | #define __OPKELE_DISCOVERY_H | 2 | #define __OPKELE_DISCOVERY_H |
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 | ||
7 | namespace opkele { | 8 | namespace 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; |
16 | string normalized_id; | 90 | string normalized_id; |
17 | string canonicalized_id; | 91 | string canonicalized_id; |
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 | ||
40 | #endif /* __OPKELE_DISCOVERY_H */ | 104 | #endif /* __OPKELE_DISCOVERY_H */ |
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 | |||
@@ -308,9 +308,28 @@ namespace opkele { | |||
308 | class unsupported : public exception { | 308 | class unsupported : public exception { |
309 | public: | 309 | public: |
310 | unsupported(OPKELE_E_PARS) | 310 | unsupported(OPKELE_E_PARS) |
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 | |||
@@ -14,47 +14,48 @@ namespace opkele { | |||
14 | * OpenID extension hooks base class | 14 | * OpenID extension hooks base class |
15 | */ | 15 | */ |
16 | class extension_t { | 16 | class extension_t { |
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. |
23 | * @param p parameters about to be submitted to server | 24 | * @param p parameters about to be submitted to server |
24 | * @param identity identity being verified. It may differ from the | 25 | * @param identity identity being verified. It may differ from the |
25 | * one available in parameters list in case of delegation | 26 | * one available in parameters list in case of delegation |
26 | * @see consumer_t::checkid_ | 27 | * @see consumer_t::checkid_ |
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. |
34 | * @param p parameters received from server | 35 | * @param p parameters received from server |
35 | * @param sp signed parameters received from server with 'openid.' | 36 | * @param sp signed parameters received from server with 'openid.' |
36 | * leader stripped | 37 | * leader stripped |
37 | * @param identity identity confirmed. May differ from the one | 38 | * @param identity identity confirmed. May differ from the one |
38 | * available in parameters list in case of delegation. May also be | 39 | * available in parameters list in case of delegation. May also be |
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. |
46 | * The hook may manipulate output parameters. It is important to | 47 | * The hook may manipulate output parameters. It is important to |
47 | * note that modified pout["signed"] is used for signing response. | 48 | * note that modified pout["signed"] is used for signing response. |
48 | * @param pin request parameters list with "openid." prefix | 49 | * @param pin request parameters list with "openid." prefix |
49 | * @param pout response parameters list without "openid." prefix | 50 | * @param pout response parameters list without "openid." prefix |
50 | * @see server_t::checkid_ | 51 | * @see server_t::checkid_ |
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 |
58 | * of pointer. | 59 | * of pointer. |
59 | */ | 60 | */ |
60 | operator extension_t*(void) { return this; } | 61 | operator extension_t*(void) { return this; } |
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 | |||
@@ -25,14 +25,14 @@ namespace opkele { | |||
25 | extension_chain_t() { } | 25 | extension_chain_t() { } |
26 | /** | 26 | /** |
27 | * Create extension chain with a single extension in it | 27 | * Create extension chain with a single extension in it |
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 | } |
37 | 37 | ||
38 | #endif /* __OPKELE_EXTENSION_CHAIN_H */ | 38 | #endif /* __OPKELE_EXTENSION_CHAIN_H */ |
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 | |||
9 | namespace 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 | |||
@@ -135,21 +135,21 @@ namespace opkele { | |||
135 | sreg_t(long fr=fields_NONE,long fo=fields_NONE,const string& pu="") | 135 | sreg_t(long fr=fields_NONE,long fo=fields_NONE,const string& pu="") |
136 | : fields_required(fr), fields_optional(fo), policy_url(pu), has_fields(0) { } | 136 | : fields_required(fr), fields_optional(fo), policy_url(pu), has_fields(0) { } |
137 | 137 | ||
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. |
153 | * @param fb field in question | 153 | * @param fb field in question |
154 | * @see fieldbit_t | 154 | * @see fieldbit_t |
155 | * @return true if the value is available | 155 | * @return true if the value is available |
@@ -192,12 +192,12 @@ namespace opkele { | |||
192 | * fields_response. | 192 | * fields_response. |
193 | * @see fields_response | 193 | * @see fields_response |
194 | * @param pin input request parameters with "openid." prefix | 194 | * @param pin input request parameters with "openid." prefix |
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 | } |
202 | 202 | ||
203 | #endif /* __OPKELE_SREG_H */ | 203 | #endif /* __OPKELE_SREG_H */ |
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 | |||
@@ -8,21 +8,26 @@ | |||
8 | 8 | ||
9 | #include <ostream> | 9 | #include <ostream> |
10 | #include <vector> | 10 | #include <vector> |
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 | ||
16 | namespace opkele { | 18 | namespace opkele { |
17 | using std::vector; | 19 | using std::vector; |
18 | using std::string; | 20 | using std::string; |
19 | using std::map; | 21 | using std::map; |
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 |
26 | */ | 31 | */ |
27 | typedef enum _mode_t { | 32 | typedef enum _mode_t { |
28 | mode_associate, | 33 | mode_associate, |
@@ -108,139 +113,95 @@ namespace opkele { | |||
108 | 113 | ||
109 | /** | 114 | /** |
110 | * the shared_ptr<> for association_t object type | 115 | * the shared_ptr<> for association_t object type |
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 | /** |
121 | * check whether the parameter is present. | 179 | * check whether the parameter is present. |
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 | /** | 201 | string append_query(const string& url,const char *prefix="openid.") const; |
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; | ||
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 | }; | 203 | }; |
172 | 204 | ||
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 | }; | ||
197 | |||
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 | |||
@@ -3,12 +3,13 @@ | |||
3 | 3 | ||
4 | #include <time.h> | 4 | #include <time.h> |
5 | #include <string> | 5 | #include <string> |
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 | ||
10 | namespace opkele { | 11 | namespace opkele { |
11 | using std::string; | 12 | using std::string; |
12 | using std::vector; | 13 | using std::vector; |
13 | 14 | ||
14 | /** | 15 | /** |
@@ -133,11 +134,18 @@ namespace opkele { | |||
133 | * @param uri URI | 134 | * @param uri URI |
134 | * @return normalized URI | 135 | * @return normalized URI |
135 | * @throw not_implemented in case of non-httpi(s) URI | 136 | * @throw not_implemented in case of non-httpi(s) URI |
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 | } |
142 | 150 | ||
143 | #endif /* __OPKELE_UTIL_H */ | 151 | #endif /* __OPKELE_UTIL_H */ |
diff --git a/lib/Makefile.am b/lib/Makefile.am index 989de28..c58ec3f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am | |||
@@ -1,12 +1,12 @@ | |||
1 | lib_LTLIBRARIES = libopkele.la | 1 | lib_LTLIBRARIES = libopkele.la |
2 | 2 | ||
3 | AM_CPPFLAGS = ${CPPFLAGS_DEBUG} | 3 | AM_CPPFLAGS = ${CPPFLAGS_DEBUG} |
4 | DEFAULT_INCLUDES = -I${top_builddir} | 4 | DEFAULT_INCLUDES = -I${top_builddir} |
5 | INCLUDES = \ | 5 | INCLUDES = \ |
6 | -I${top_srcdir}/include/ \ | 6 | -I${top_builddir}/include/ -I${top_srcdir}/include/ \ |
7 | ${KONFORKA_CFLAGS} \ | 7 | ${KONFORKA_CFLAGS} \ |
8 | ${OPENSSL_CFLAGS} \ | 8 | ${OPENSSL_CFLAGS} \ |
9 | ${LIBCURL_CPPFLAGS} \ | 9 | ${LIBCURL_CPPFLAGS} \ |
10 | ${PCRE_CFLAGS} ${EXPAT_CFLAGS} ${TIDY_CFLAGS} | 10 | ${PCRE_CFLAGS} ${EXPAT_CFLAGS} ${TIDY_CFLAGS} |
11 | libopkele_la_LIBADD = \ | 11 | libopkele_la_LIBADD = \ |
12 | ${LIBCURL} \ | 12 | ${LIBCURL} \ |
@@ -23,9 +23,12 @@ libopkele_la_SOURCES = \ | |||
23 | consumer.cc \ | 23 | consumer.cc \ |
24 | exception.cc \ | 24 | exception.cc \ |
25 | extension.cc \ | 25 | extension.cc \ |
26 | sreg.cc \ | 26 | sreg.cc \ |
27 | extension_chain.cc \ | 27 | extension_chain.cc \ |
28 | curl.cc expat.cc \ | 28 | curl.cc expat.cc \ |
29 | discovery.cc | 29 | discovery.cc \ |
30 | basic_rp.cc \ | ||
31 | prequeue_rp.cc \ | ||
32 | openid_message.cc | ||
30 | libopkele_la_LDFLAGS = \ | 33 | libopkele_la_LDFLAGS = \ |
31 | -version-info 2:0:0 | 34 | -version-info 2:0:0 |
diff --git a/lib/basic_rp.cc b/lib/basic_rp.cc new file mode 100644 index 0000000..763a391 --- a/dev/null +++ b/lib/basic_rp.cc | |||
@@ -0,0 +1,311 @@ | |||
1 | #include <openssl/sha.h> | ||
2 | #include <openssl/hmac.h> | ||
3 | #include <opkele/basic_rp.h> | ||
4 | #include <opkele/exception.h> | ||
5 | #include <opkele/uris.h> | ||
6 | #include <opkele/data.h> | ||
7 | #include <opkele/util.h> | ||
8 | #include <opkele/curl.h> | ||
9 | |||
10 | namespace opkele { | ||
11 | |||
12 | static void dh_get_secret( | ||
13 | secret_t& secret, const basic_openid_message& om, | ||
14 | const char *exp_assoc, const char *exp_sess, | ||
15 | util::dh_t& dh, | ||
16 | size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*) ) try { | ||
17 | if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess) | ||
18 | throw bad_input(OPKELE_CP_ "Unexpected associate response"); | ||
19 | util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public")); | ||
20 | vector<unsigned char> ck(DH_size(dh)+1); | ||
21 | unsigned char *ckptr = &(ck.front())+1; | ||
22 | int cklen = DH_compute_key(ckptr,s_pub,dh); | ||
23 | if(cklen<0) | ||
24 | throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); | ||
25 | if(cklen && (*ckptr)&0x80) { | ||
26 | (*(--ckptr))=0; ++cklen; } | ||
27 | unsigned char key_digest[d_len]; | ||
28 | secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key")); | ||
29 | }catch(opkele::failed_lookup& ofl) { | ||
30 | throw bad_input(OPKELE_CP_ "Incoherent response from OP"); | ||
31 | } OPKELE_RETHROW | ||
32 | |||
33 | static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) { | ||
34 | util::curl_pick_t curl = util::curl_pick_t::easy_init(); | ||
35 | if(!curl) | ||
36 | throw exception_curl(OPKELE_CP_ "failed to initialize curl"); | ||
37 | string request = inm.query_string(); | ||
38 | CURLcode r; | ||
39 | (r=curl.misc_sets()) | ||
40 | || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str())) | ||
41 | || (r=curl.easy_setopt(CURLOPT_POST,1)) | ||
42 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) | ||
43 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) | ||
44 | || (r=curl.set_write()); | ||
45 | if(r) | ||
46 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); | ||
47 | if( (r=curl.easy_perform()) ) | ||
48 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); | ||
49 | oum.from_keyvalues(curl.response); | ||
50 | } | ||
51 | |||
52 | |||
53 | assoc_t basic_RP::associate(const string& OP) { | ||
54 | util::dh_t dh = DH_new(); | ||
55 | if(!dh) | ||
56 | throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); | ||
57 | dh->p = util::dec_to_bignum(data::_default_p); | ||
58 | dh->g = util::dec_to_bignum(data::_default_g); | ||
59 | if(!DH_generate_key(dh)) | ||
60 | throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); | ||
61 | openid_message_t req; | ||
62 | req.set_field("ns",OIURI_OPENID20); | ||
63 | req.set_field("mode","associate"); | ||
64 | req.set_field("dh_modulus",util::bignum_to_base64(dh->p)); | ||
65 | req.set_field("dh_gen",util::bignum_to_base64(dh->g)); | ||
66 | req.set_field("dh_consumer_public",util::bignum_to_base64(dh->pub_key)); | ||
67 | openid_message_t res; | ||
68 | req.set_field("assoc_type","HMAC-SHA256"); | ||
69 | req.set_field("session_type","DH-SHA256"); | ||
70 | secret_t secret; | ||
71 | int expires_in; | ||
72 | try { | ||
73 | direct_request(res,req,OP); | ||
74 | dh_get_secret( secret, res, | ||
75 | "HMAC-SHA256", "DH-SHA256", | ||
76 | dh, SHA256_DIGEST_LENGTH, SHA256 ); | ||
77 | expires_in = util::string_to_long(res.get_field("expires_in")); | ||
78 | }catch(exception& e) { | ||
79 | try { | ||
80 | req.set_field("assoc_type","HMAC-SHA1"); | ||
81 | req.set_field("session_type","DH-SHA1"); | ||
82 | direct_request(res,req,OP); | ||
83 | dh_get_secret( secret, res, | ||
84 | "HMAC-SHA1", "DH-SHA1", | ||
85 | dh, SHA_DIGEST_LENGTH, SHA1 ); | ||
86 | expires_in = util::string_to_long(res.get_field("expires_in")); | ||
87 | }catch(bad_input& e) { | ||
88 | throw dumb_RP(OPKELE_CP_ "OP failed to supply an association"); | ||
89 | } | ||
90 | } | ||
91 | return store_assoc( | ||
92 | OP, res.get_field("assoc_handle"), | ||
93 | res.get_field("assoc_type"), secret, | ||
94 | expires_in ); | ||
95 | } | ||
96 | |||
97 | basic_openid_message& basic_RP::checkid_( | ||
98 | basic_openid_message& rv, | ||
99 | mode_t mode, | ||
100 | const string& return_to,const string& realm, | ||
101 | extension_t *ext) { | ||
102 | rv.reset_fields(); | ||
103 | rv.set_field("ns",OIURI_OPENID20); | ||
104 | if(mode==mode_checkid_immediate) | ||
105 | rv.set_field("mode","checkid_immediate"); | ||
106 | else if(mode==mode_checkid_setup) | ||
107 | rv.set_field("mode","checkid_setup"); | ||
108 | else | ||
109 | throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); | ||
110 | if(realm.empty() && return_to.empty()) | ||
111 | throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty"); | ||
112 | if(!realm.empty()) { | ||
113 | rv.set_field("realm",realm); | ||
114 | rv.set_field("trust_root",realm); | ||
115 | } | ||
116 | if(!return_to.empty()) | ||
117 | rv.set_field("return_to",return_to); | ||
118 | const openid_endpoint_t& ep = get_endpoint(); | ||
119 | rv.set_field("claimed_id",ep.claimed_id); | ||
120 | rv.set_field("identity",ep.local_id); | ||
121 | try { | ||
122 | rv.set_field("assoc_handle",find_assoc(ep.uri)->handle()); | ||
123 | }catch(dumb_RP& drp) { | ||
124 | }catch(failed_lookup& fl) { | ||
125 | try { | ||
126 | rv.set_field("assoc_handle",associate(ep.uri)->handle()); | ||
127 | }catch(dumb_RP& drp) { } | ||
128 | } OPKELE_RETHROW | ||
129 | if(ext) ext->checkid_hook(rv); | ||
130 | return rv; | ||
131 | } | ||
132 | |||
133 | class signed_part_message_proxy : public basic_openid_message { | ||
134 | public: | ||
135 | const basic_openid_message& x; | ||
136 | set<string> signeds; | ||
137 | |||
138 | signed_part_message_proxy(const basic_openid_message& xx) : x(xx) { | ||
139 | const string& slist = x.get_field("signed"); | ||
140 | string::size_type p = 0; | ||
141 | while(true) { | ||
142 | string::size_type co = slist.find(',',p); | ||
143 | string f = (co==string::npos) | ||
144 | ?slist.substr(p):slist.substr(p,co-p); | ||
145 | signeds.insert(f); | ||
146 | if(co==string::npos) break; | ||
147 | p = co+1; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | bool has_field(const string& n) const { | ||
152 | return signeds.find(n)!=signeds.end() && x.has_field(n); } | ||
153 | const string& get_field(const string& n) const { | ||
154 | if(signeds.find(n)==signeds.end()) | ||
155 | throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed"); | ||
156 | return x.get_field(n); } | ||
157 | |||
158 | fields_iterator fields_begin() const { | ||
159 | return signeds.begin(); } | ||
160 | fields_iterator fields_end() const { | ||
161 | return signeds.end(); } | ||
162 | }; | ||
163 | |||
164 | static void parse_query(const string& u,string::size_type q, | ||
165 | map<string,string>& p) { | ||
166 | if(q==string::npos) | ||
167 | return; | ||
168 | assert(u[q]=='?'); | ||
169 | ++q; | ||
170 | string::size_type l = u.size(); | ||
171 | while(q<l) { | ||
172 | string::size_type eq = u.find('=',q); | ||
173 | string::size_type am = u.find('&',q); | ||
174 | if(am==string::npos) { | ||
175 | if(eq==string::npos) { | ||
176 | p[""] = u.substr(q); | ||
177 | }else{ | ||
178 | p[u.substr(q,eq-q)] = u.substr(eq+1); | ||
179 | } | ||
180 | break; | ||
181 | }else{ | ||
182 | if(eq==string::npos || eq>am) { | ||
183 | p[""] = u.substr(q,eq-q); | ||
184 | }else{ | ||
185 | p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1); | ||
186 | } | ||
187 | q = ++am; | ||
188 | } | ||
189 | } | ||
190 | } | ||
191 | |||
192 | void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) { | ||
193 | bool o2 = om.has_field("ns") | ||
194 | && om.get_field("ns")==OIURI_OPENID20; | ||
195 | if( (!o2) && om.has_field("user_setup_url")) | ||
196 | throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided", | ||
197 | om.get_field("user_setup_url")); | ||
198 | string m = om.get_field("mode"); | ||
199 | if(o2 && m=="setup_needed") | ||
200 | throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided"); | ||
201 | if(m=="cancel") | ||
202 | throw id_res_cancel(OPKELE_CP_ "authentication cancelled"); | ||
203 | bool go_dumb=false; | ||
204 | try { | ||
205 | string OP = o2 | ||
206 | ?om.get_field("op_endpoint") | ||
207 | :get_endpoint().uri; | ||
208 | assoc_t assoc = retrieve_assoc( | ||
209 | OP,om.get_field("assoc_handle")); | ||
210 | if(om.get_field("sig")!=util::base64_signature(assoc,om)) | ||
211 | throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); | ||
212 | }catch(dumb_RP& drp) { | ||
213 | go_dumb=true; | ||
214 | }catch(failed_lookup& e) { | ||
215 | go_dumb=true; | ||
216 | } OPKELE_RETHROW | ||
217 | if(go_dumb) { | ||
218 | try { | ||
219 | string OP = o2 | ||
220 | ?om.get_field("op_endpoint") | ||
221 | :get_endpoint().uri; | ||
222 | check_authentication(OP,om); | ||
223 | }catch(failed_check_authentication& fca) { | ||
224 | throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); | ||
225 | } OPKELE_RETHROW | ||
226 | } | ||
227 | signed_part_message_proxy signeds(om); | ||
228 | if(o2) { | ||
229 | check_nonce(om.get_field("op_endpoint"), | ||
230 | om.get_field("response_nonce")); | ||
231 | static const char *mustsign[] = { | ||
232 | "op_endpoint", "return_to", "response_nonce", "assoc_handle", | ||
233 | "claimed_id", "identity" }; | ||
234 | for(int ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) { | ||
235 | if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms])) | ||
236 | throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs"); | ||
237 | } | ||
238 | if( ( | ||
239 | (om.has_field("claimed_id")?1:0) | ||
240 | ^ | ||
241 | (om.has_field("identity")?1:0) | ||
242 | )&1 ) | ||
243 | throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent"); | ||
244 | |||
245 | string turl = util::rfc_3986_normalize_uri(get_this_url()); | ||
246 | util::strip_uri_fragment_part(turl); | ||
247 | string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to")); | ||
248 | util::strip_uri_fragment_part(rurl); | ||
249 | string::size_type | ||
250 | tq = turl.find('?'), rq = rurl.find('?'); | ||
251 | if( | ||
252 | ((tq==string::npos)?turl:turl.substr(0,tq)) | ||
253 | != | ||
254 | ((rq==string::npos)?rurl:rurl.substr(0,rq)) | ||
255 | ) | ||
256 | throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url"); | ||
257 | map<string,string> tp; parse_query(turl,tq,tp); | ||
258 | map<string,string> rp; parse_query(rurl,rq,rp); | ||
259 | for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) { | ||
260 | map<string,string>::const_iterator tpi = tp.find(rpi->first); | ||
261 | if(tpi==tp.end()) | ||
262 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request"); | ||
263 | if(tpi->second!=rpi->second) | ||
264 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request"); | ||
265 | } | ||
266 | |||
267 | if(om.has_field("claimed_id")) { | ||
268 | verify_OP( | ||
269 | om.get_field("op_endpoint"), | ||
270 | om.get_field("claimed_id"), | ||
271 | om.get_field("identity") ); | ||
272 | } | ||
273 | |||
274 | } | ||
275 | if(ext) ext->id_res_hook(om,signeds); | ||
276 | } | ||
277 | |||
278 | class check_auth_message_proxy : public basic_openid_message { | ||
279 | public: | ||
280 | const basic_openid_message& x; | ||
281 | |||
282 | check_auth_message_proxy(const basic_openid_message& xx) : x(xx) { } | ||
283 | |||
284 | bool has_field(const string& n) const { return x.has_field(n); } | ||
285 | const string& get_field(const string& n) const { | ||
286 | static const string checkauthmode="check_authentication"; | ||
287 | return (n=="mode")?checkauthmode:x.get_field(n); } | ||
288 | bool has_ns(const string& uri) const {return x.has_ns(uri); } | ||
289 | string get_ns(const string& uri) const { return x.get_ns(uri); } | ||
290 | fields_iterator fields_begin() const { | ||
291 | return x.fields_begin(); } | ||
292 | fields_iterator fields_end() const { | ||
293 | return x.fields_end(); } | ||
294 | }; | ||
295 | |||
296 | void basic_RP::check_authentication(const string& OP, | ||
297 | const basic_openid_message& om){ | ||
298 | openid_message_t res; | ||
299 | direct_request(res,check_auth_message_proxy(om),OP); | ||
300 | if(res.has_field("is_valid")) { | ||
301 | if(res.get_field("is_valid")=="true") { | ||
302 | if(res.has_field("invalidate_handle")) | ||
303 | invalidate_assoc(OP,res.get_field("invalidate_handle")); | ||
304 | return; | ||
305 | } | ||
306 | } | ||
307 | throw failed_check_authentication( | ||
308 | OPKELE_CP_ "failed to verify response"); | ||
309 | } | ||
310 | |||
311 | } | ||
diff --git a/lib/consumer.cc b/lib/consumer.cc index 3c3b4f8..ebda262 100644 --- a/lib/consumer.cc +++ b/lib/consumer.cc | |||
@@ -151,13 +151,13 @@ namespace opkele { | |||
151 | string ah = find_assoc(server)->handle(); | 151 | string ah = find_assoc(server)->handle(); |
152 | p["assoc_handle"] = ah; | 152 | p["assoc_handle"] = ah; |
153 | }catch(failed_lookup& fl) { | 153 | }catch(failed_lookup& fl) { |
154 | string ah = associate(server)->handle(); | 154 | string ah = associate(server)->handle(); |
155 | p["assoc_handle"] = ah; | 155 | p["assoc_handle"] = ah; |
156 | } | 156 | } |
157 | if(ext) ext->checkid_hook(p,identity); | 157 | if(ext) ext->checkid_hook(p); |
158 | return p.append_query(server); | 158 | return p.append_query(server); |
159 | } | 159 | } |
160 | 160 | ||
161 | void consumer_t::id_res(const params_t& pin,const string& identity,extension_t *ext) { | 161 | void consumer_t::id_res(const params_t& pin,const string& identity,extension_t *ext) { |
162 | if(pin.has_param("openid.user_setup_url")) | 162 | if(pin.has_param("openid.user_setup_url")) |
163 | throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",pin.get_param("openid.user_setup_url")); | 163 | throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",pin.get_param("openid.user_setup_url")); |
@@ -219,13 +219,13 @@ namespace opkele { | |||
219 | try { | 219 | try { |
220 | check_authentication(server,p); | 220 | check_authentication(server,p); |
221 | }catch(failed_check_authentication& fca) { | 221 | }catch(failed_check_authentication& fca) { |
222 | throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); | 222 | throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); |
223 | } | 223 | } |
224 | } | 224 | } |
225 | if(ext) ext->id_res_hook(pin,ps,identity); | 225 | if(ext) ext->id_res_hook(pin,ps); |
226 | } | 226 | } |
227 | 227 | ||
228 | void consumer_t::check_authentication(const string& server,const params_t& p) { | 228 | void consumer_t::check_authentication(const string& server,const params_t& p) { |
229 | string request = "openid.mode=check_authentication"; | 229 | string request = "openid.mode=check_authentication"; |
230 | for(params_t::const_iterator i=p.begin();i!=p.end();++i) { | 230 | for(params_t::const_iterator i=p.begin();i!=p.end();++i) { |
231 | if(i->first!="openid.mode") { | 231 | if(i->first!="openid.mode") { |
diff --git a/lib/discovery.cc b/lib/discovery.cc index d868308..93409f4 100644 --- a/lib/discovery.cc +++ b/lib/discovery.cc | |||
@@ -15,16 +15,33 @@ | |||
15 | 15 | ||
16 | namespace opkele { | 16 | namespace opkele { |
17 | using std::list; | 17 | using std::list; |
18 | using xrd::XRD_t; | 18 | using xrd::XRD_t; |
19 | using xrd::service_t; | 19 | using xrd::service_t; |
20 | 20 | ||
21 | /* TODO: the whole discovery thing needs cleanup and optimization due to | ||
22 | * many changes of concept. */ | ||
23 | |||
21 | static const char *whitespace = " \t\r\n"; | 24 | static const char *whitespace = " \t\r\n"; |
22 | static const char *i_leaders = "=@+$!("; | 25 | static const char *i_leaders = "=@+$!("; |
23 | static const size_t max_html = 16384; | 26 | static const size_t max_html = 16384; |
24 | 27 | ||
28 | static const struct service_type_t { | ||
29 | const char *uri; | ||
30 | const char *forceid; | ||
31 | } service_types[] = { | ||
32 | { STURI_OPENID20_OP, IDURI_SELECT20 }, | ||
33 | { STURI_OPENID20, 0 }, | ||
34 | { STURI_OPENID11, 0 }, | ||
35 | { STURI_OPENID10, 0 } | ||
36 | }; | ||
37 | enum { | ||
38 | st_index_1 = 2, st_index_2 = 1 | ||
39 | }; | ||
40 | |||
41 | |||
25 | static inline bool is_qelement(const XML_Char *n,const char *qen) { | 42 | static inline bool is_qelement(const XML_Char *n,const char *qen) { |
26 | return !strcasecmp(n,qen); | 43 | return !strcasecmp(n,qen); |
27 | } | 44 | } |
28 | static inline bool is_element(const XML_Char *n,const char *en) { | 45 | static inline bool is_element(const XML_Char *n,const char *en) { |
29 | if(!strcasecmp(n,en)) return true; | 46 | if(!strcasecmp(n,en)) return true; |
30 | int nl = strlen(n), enl = strlen(en); | 47 | int nl = strlen(n), enl = strlen(en); |
@@ -45,13 +62,13 @@ namespace opkele { | |||
45 | 62 | ||
46 | class idigger_t : public util::curl_t, public util::expat_t { | 63 | class idigger_t : public util::curl_t, public util::expat_t { |
47 | public: | 64 | public: |
48 | string xri_proxy; | 65 | string xri_proxy; |
49 | 66 | ||
50 | enum { | 67 | enum { |
51 | xmode_html = 1, xmode_xrd = 2 | 68 | xmode_html = 1, xmode_xrd = 2, xmode_cid = 4 |
52 | }; | 69 | }; |
53 | int xmode; | 70 | int xmode; |
54 | 71 | ||
55 | string xrds_location; | 72 | string xrds_location; |
56 | string http_content_type; | 73 | string http_content_type; |
57 | service_t html_openid1; | 74 | service_t html_openid1; |
@@ -81,81 +98,120 @@ namespace opkele { | |||
81 | ; | 98 | ; |
82 | if(r) | 99 | if(r) |
83 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); | 100 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); |
84 | } | 101 | } |
85 | ~idigger_t() throw() { } | 102 | ~idigger_t() throw() { } |
86 | 103 | ||
87 | void discover(idiscovery_t& result,const string& identity) { | 104 | string discover(endpoint_discovery_iterator& oi,const string& identity) { |
88 | result.clear(); | 105 | string rv; |
106 | idiscovery_t idis; | ||
89 | string::size_type fsc = identity.find_first_not_of(whitespace); | 107 | string::size_type fsc = identity.find_first_not_of(whitespace); |
90 | if(fsc==string::npos) | 108 | if(fsc==string::npos) |
91 | throw bad_input(OPKELE_CP_ "whtiespace-only identity"); | 109 | throw bad_input(OPKELE_CP_ "whitespace-only identity"); |
92 | string::size_type lsc = identity.find_last_not_of(whitespace); | 110 | string::size_type lsc = identity.find_last_not_of(whitespace); |
93 | assert(lsc!=string::npos); | 111 | assert(lsc!=string::npos); |
94 | if(!strncasecmp(identity.c_str()+fsc,"xri://",sizeof("xri://")-1)) | 112 | if(!strncasecmp(identity.c_str()+fsc,"xri://",sizeof("xri://")-1)) |
95 | fsc += sizeof("xri://")-1; | 113 | fsc += sizeof("xri://")-1; |
96 | if((fsc+1)>=lsc) | 114 | if((fsc+1)>=lsc) |
97 | throw bad_input(OPKELE_CP_ "not a character of importance in identity"); | 115 | throw bad_input(OPKELE_CP_ "not a character of importance in identity"); |
98 | string id(identity,fsc,lsc-fsc+1); | 116 | string id(identity,fsc,lsc-fsc+1); |
117 | idis.clear(); | ||
99 | if(strchr(i_leaders,id[0])) { | 118 | if(strchr(i_leaders,id[0])) { |
100 | result.normalized_id = id; | 119 | /* TODO: further normalize xri identity? Like folding case |
101 | result.xri_identity = true; | 120 | * or whatever... */ |
102 | /* TODO: further canonicalize xri identity? Like folding case or whatever... */ | 121 | rv = idis.normalized_id = id; |
103 | discover_at( | 122 | idis.xri_identity = true; |
104 | result, | 123 | set<string> cids; |
105 | xri_proxy + util::url_encode(id)+ | 124 | for(const struct service_type_t *st=service_types; |
106 | "?_xrd_r=application/xrd+xml;sep=false", xmode_xrd); | 125 | st<&service_types[sizeof(service_types)/sizeof(*service_types)];++st) { |
107 | if(status_code!=100) | 126 | idis.clear(); |
108 | throw failed_xri_resolution(OPKELE_CP_ | 127 | discover_at( idis, |
109 | "XRI resolution failed with '"+status_string+"' message",status_code); | 128 | xri_proxy + util::url_encode(id)+ |
110 | if(result.xrd.canonical_ids.empty()) | 129 | "?_xrd_t="+util::url_encode(st->uri)+ |
111 | throw opkele::failed_discovery(OPKELE_CP_ "No CanonicalID for XRI identity found"); | 130 | "&_xrd_r=application/xrd%2Bxml" |
112 | result.canonicalized_id = result.xrd.canonical_ids.begin()->second; | 131 | ";sep=true;refs=true", |
132 | xmode_xrd ); | ||
133 | if(status_code==241) continue; | ||
134 | if(status_code!=100) | ||
135 | throw failed_xri_resolution(OPKELE_CP_ | ||
136 | "XRI resolution failed with '"+status_string+"' message" | ||
137 | ", while looking for SEP with type '"+st->uri+"'", status_code); | ||
138 | if(idis.xrd.canonical_ids.empty()) | ||
139 | throw opkele::failed_discovery(OPKELE_CP_ "No CanonicalID found for XRI identity found"); | ||
140 | string cid = idis.xrd.canonical_ids.begin()->second; | ||
141 | if(cids.find(cid)==cids.end()) { | ||
142 | cids.insert(cid); | ||
143 | idis.clear(); | ||
144 | discover_at( idis, | ||
145 | xri_proxy + util::url_encode(id)+ | ||
146 | "?_xrd_t="+util::url_encode(st->uri)+ | ||
147 | "&_xrd_r=application/xrd%2Bxml" | ||
148 | ";sep=true;refs=true", | ||
149 | xmode_xrd ); | ||
150 | if(status_code==241) continue; | ||
151 | if(status_code!=100) | ||
152 | throw failed_xri_resolution(OPKELE_CP_ | ||
153 | "XRI resolution failed with '"+status_string+"' message" | ||
154 | ", while looking for SEP with type '"+st->uri+"'" | ||
155 | " on canonical id", status_code); | ||
156 | } | ||
157 | idis.canonicalized_id = cid; | ||
158 | queue_endpoints(oi,idis,st); | ||
159 | } | ||
113 | }else{ | 160 | }else{ |
114 | result.xri_identity = false; | 161 | idis.xri_identity = false; |
115 | if(id.find("://")==string::npos) | 162 | if(id.find("://")==string::npos) |
116 | id.insert(0,"http://"); | 163 | id.insert(0,"http://"); |
117 | string::size_type fp = id.find('#'); | 164 | string::size_type fp = id.find('#'); |
118 | if(fp!=string::npos) { | 165 | if(fp!=string::npos) { |
119 | string::size_type qp = id.find('?'); | 166 | string::size_type qp = id.find('?'); |
120 | if(qp==string::npos || qp<fp) | 167 | if(qp==string::npos || qp<fp) |
121 | id.erase(fp); | 168 | id.erase(fp); |
122 | else if(qp>fp) | 169 | else if(qp>fp) |
123 | id.erase(fp,qp-fp); | 170 | id.erase(fp,qp-fp); |
124 | } | 171 | } |
125 | result.normalized_id = util::rfc_3986_normalize_uri(id); | 172 | rv = idis.normalized_id = util::rfc_3986_normalize_uri(id); |
126 | discover_at(result,id,xmode_html|xmode_xrd); | 173 | discover_at(idis,id,xmode_html|xmode_xrd); |
127 | const char * eu = 0; | 174 | const char * eu = 0; |
128 | CURLcode r = easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu); | 175 | CURLcode r = easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu); |
129 | if(r) | 176 | if(r) |
130 | throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r); | 177 | throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r); |
131 | result.canonicalized_id = util::rfc_3986_normalize_uri(eu); /* XXX: strip fragment part? */ | 178 | string cid = util::strip_uri_fragment_part( idis.canonicalized_id = util::rfc_3986_normalize_uri(eu) ); |
132 | if(xrds_location.empty()) { | 179 | if(xrds_location.empty()) { |
133 | html2xrd(result.xrd); | 180 | html2xrd(oi,idis); |
134 | }else{ | 181 | }else{ |
135 | discover_at(result,xrds_location,xmode_xrd); | 182 | idis.clear(); |
136 | if(result.xrd.empty()) | 183 | idis.canonicalized_id = cid; |
137 | html2xrd(result.xrd); | 184 | discover_at(idis,xrds_location,xmode_xrd); |
185 | if(idis.xrd.empty()) | ||
186 | html2xrd(oi,idis); | ||
187 | else{ | ||
188 | for(const service_type_t *st=service_types; | ||
189 | st<&service_types[sizeof(service_types)/sizeof(*service_types)];++st) | ||
190 | queue_endpoints(oi,idis,st); | ||
191 | } | ||
138 | } | 192 | } |
139 | } | 193 | } |
194 | return rv; | ||
140 | } | 195 | } |
141 | 196 | ||
142 | void discover_at(idiscovery_t& result,const string& url,int xm) { | 197 | void discover_at(idiscovery_t& idis,const string& url,int xm) { |
198 | DOUT_("Doing discovery at " << url); | ||
143 | CURLcode r = easy_setopt(CURLOPT_URL,url.c_str()); | 199 | CURLcode r = easy_setopt(CURLOPT_URL,url.c_str()); |
144 | if(r) | 200 | if(r) |
145 | throw exception_curl(OPKELE_CP_ "failed to set culry urlie",r); | 201 | throw exception_curl(OPKELE_CP_ "failed to set culry urlie",r); |
146 | 202 | ||
147 | http_content_type.clear(); | 203 | http_content_type.clear(); |
148 | xmode = xm; | 204 | xmode = xm; |
149 | prepare_to_parse(); | 205 | prepare_to_parse(); |
150 | if(xmode&xmode_html) { | 206 | if(xmode&xmode_html) { |
151 | xrds_location.clear(); | 207 | xrds_location.clear(); |
152 | save_html.clear(); | 208 | save_html.clear(); |
153 | save_html.reserve(max_html); | 209 | save_html.reserve(max_html); |
154 | } | 210 | } |
155 | xrd = &result.xrd; | 211 | xrd = &idis.xrd; |
156 | 212 | ||
157 | r = easy_perform(); | 213 | r = easy_perform(); |
158 | if(r && r!=CURLE_WRITE_ERROR) | 214 | if(r && r!=CURLE_WRITE_ERROR) |
159 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); | 215 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); |
160 | 216 | ||
161 | if(!parser_choked) { | 217 | if(!parser_choked) { |
@@ -196,23 +252,27 @@ namespace opkele { | |||
196 | if(xmode&xmode_html) { | 252 | if(xmode&xmode_html) { |
197 | html_openid1.clear(); html_openid2.clear(); | 253 | html_openid1.clear(); html_openid2.clear(); |
198 | parser_choked = false; | 254 | parser_choked = false; |
199 | } | 255 | } |
200 | 256 | ||
201 | cdata = 0; xrd_service = 0; skipping = 0; | 257 | cdata = 0; xrd_service = 0; skipping = 0; |
258 | pt_stack.clear(); | ||
202 | status_code = 100; status_string.clear(); | 259 | status_code = 100; status_string.clear(); |
203 | } | 260 | } |
204 | 261 | ||
205 | void html2xrd(XRD_t& x) { | 262 | void html2xrd(endpoint_discovery_iterator& oi,idiscovery_t& id) { |
206 | if(!html_openid1.uris.empty()) { | 263 | XRD_t& x = id.xrd; |
207 | html_openid1.types.insert(STURI_OPENID11); | ||
208 | x.services.add(-1,html_openid1); | ||
209 | } | ||
210 | if(!html_openid2.uris.empty()) { | 264 | if(!html_openid2.uris.empty()) { |
211 | html_openid2.types.insert(STURI_OPENID20); | 265 | html_openid2.types.insert(STURI_OPENID20); |
212 | x.services.add(-1,html_openid2); | 266 | x.services.add(-1,html_openid2); |
267 | queue_endpoints(oi,id,&service_types[st_index_2]); | ||
268 | } | ||
269 | if(!html_openid1.uris.empty()) { | ||
270 | html_openid1.types.insert(STURI_OPENID11); | ||
271 | x.services.add(-1,html_openid1); | ||
272 | queue_endpoints(oi,id,&service_types[st_index_1]); | ||
213 | } | 273 | } |
214 | } | 274 | } |
215 | 275 | ||
216 | size_t write(void *p,size_t s,size_t nm) { | 276 | size_t write(void *p,size_t s,size_t nm) { |
217 | /* TODO: limit total size */ | 277 | /* TODO: limit total size */ |
218 | size_t bytes = s*nm; | 278 | size_t bytes = s*nm; |
@@ -307,13 +367,14 @@ namespace opkele { | |||
307 | if(!strcasecmp(*(a++),"code")) { | 367 | if(!strcasecmp(*(a++),"code")) { |
308 | if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) { | 368 | if(sscanf(*(a++),"%ld",&status_code)==1 && status_code!=100) { |
309 | cdata = &status_string; | 369 | cdata = &status_string; |
310 | pt_stack.push_back(n); | 370 | pt_stack.push_back(n); |
311 | break; | 371 | break; |
312 | } | 372 | } |
313 | } | 373 | }else |
374 | ++a; | ||
314 | } | 375 | } |
315 | }else if(is_qelement(n,NSURI_XRD "\tExpires")) { | 376 | }else if(is_qelement(n,NSURI_XRD "\tExpires")) { |
316 | assert(xrd); | 377 | assert(xrd); |
317 | cdata_buf.clear(); | 378 | cdata_buf.clear(); |
318 | cdata = &cdata_buf; | 379 | cdata = &cdata_buf; |
319 | }else if(xmode&xmode_html) { | 380 | }else if(xmode&xmode_html) { |
@@ -433,14 +494,44 @@ namespace opkele { | |||
433 | } | 494 | } |
434 | }else if(is_element(n,"body")) { | 495 | }else if(is_element(n,"body")) { |
435 | skipping = -1; | 496 | skipping = -1; |
436 | } | 497 | } |
437 | } | 498 | } |
438 | 499 | ||
500 | void queue_endpoints(endpoint_discovery_iterator& oi, | ||
501 | const idiscovery_t &id, | ||
502 | const service_type_t *st) { | ||
503 | openid_endpoint_t ep; | ||
504 | ep.claimed_id = id.canonicalized_id; | ||
505 | for(xrd::services_t::const_iterator isvc=id.xrd.services.begin(); | ||
506 | isvc!=id.xrd.services.end(); ++isvc) { | ||
507 | const xrd::service_t svc = isvc->second; | ||
508 | if(svc.types.find(st->uri)==svc.types.end()) continue; | ||
509 | for(xrd::uris_t::const_iterator iu=svc.uris.begin();iu!=svc.uris.end();++iu) { | ||
510 | ep.uri = iu->second; | ||
511 | if(st->forceid) { | ||
512 | ep.local_id = ep.claimed_id = st->forceid; | ||
513 | *(oi++) = ep; | ||
514 | }else{ | ||
515 | if(svc.local_ids.empty()) { | ||
516 | ep.local_id = ep.claimed_id; | ||
517 | *(oi++) = ep; | ||
518 | }else{ | ||
519 | for(xrd::local_ids_t::const_iterator ilid=svc.local_ids.begin(); | ||
520 | ilid!=svc.local_ids.end(); ++ilid) { | ||
521 | ep.local_id = ilid->second; | ||
522 | *(oi++) = ep; | ||
523 | } | ||
524 | } | ||
525 | } | ||
526 | } | ||
527 | } | ||
528 | } | ||
529 | |||
439 | }; | 530 | }; |
440 | 531 | ||
441 | void idiscover(idiscovery_t& result,const string& identity) { | 532 | string idiscover(endpoint_discovery_iterator oi,const string& identity) { |
442 | idigger_t idigger; | 533 | idigger_t idigger; |
443 | idigger.discover(result,identity); | 534 | return idigger.discover(oi,identity); |
444 | } | 535 | } |
445 | 536 | ||
446 | } | 537 | } |
diff --git a/lib/extension.cc b/lib/extension.cc index 8f22562..6451249 100644 --- a/lib/extension.cc +++ b/lib/extension.cc | |||
@@ -1,15 +1,15 @@ | |||
1 | #include <opkele/exception.h> | 1 | #include <opkele/exception.h> |
2 | #include <opkele/extension.h> | 2 | #include <opkele/extension.h> |
3 | 3 | ||
4 | namespace opkele { | 4 | namespace opkele { |
5 | 5 | ||
6 | void extension_t::checkid_hook(params_t& /* p */,const string& /* identity */ ) { | 6 | void extension_t::checkid_hook(basic_openid_message&) { |
7 | throw not_implemented(OPKELE_CP_ "Consumer checkid_hook not implemented"); | 7 | throw not_implemented(OPKELE_CP_ "Consumer checkid_hook not implemented"); |
8 | } | 8 | } |
9 | void extension_t::id_res_hook(const params_t& /* p */,const params_t& /* sp */,const string& /* identity */) { | 9 | void extension_t::id_res_hook(const basic_openid_message&,const basic_openid_message&) { |
10 | throw not_implemented(OPKELE_CP_ "Consumer id_res_hook not implemented"); | 10 | throw not_implemented(OPKELE_CP_ "Consumer id_res_hook not implemented"); |
11 | } | 11 | } |
12 | void extension_t::checkid_hook(const params_t& /* pin */,params_t& /* pout */) { | 12 | void extension_t::checkid_hook(const basic_openid_message&,basic_openid_message&) { |
13 | throw not_implemented(OPKELE_CP_ "Server checkid_hook not implemented"); | 13 | throw not_implemented(OPKELE_CP_ "Server checkid_hook not implemented"); |
14 | } | 14 | } |
15 | } | 15 | } |
diff --git a/lib/extension_chain.cc b/lib/extension_chain.cc index 16537dc..5c2afd9 100644 --- a/lib/extension_chain.cc +++ b/lib/extension_chain.cc | |||
@@ -1,16 +1,16 @@ | |||
1 | #include <cstdarg> | 1 | #include <cstdarg> |
2 | #include <opkele/extension_chain.h> | 2 | #include <opkele/extension_chain.h> |
3 | 3 | ||
4 | namespace opkele { | 4 | namespace opkele { |
5 | 5 | ||
6 | void extension_chain_t::checkid_hook(params_t& p,const string& identity) { | 6 | void extension_chain_t::checkid_hook(basic_openid_message& om){ |
7 | for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(p,identity); | 7 | for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(om); |
8 | } | 8 | } |
9 | void extension_chain_t::id_res_hook(const params_t& p,const params_t& sp,const string& identity) { | 9 | void extension_chain_t::id_res_hook(const basic_openid_message& om,const basic_openid_message& sp) { |
10 | for(iterator i=begin();i!=end();++i) (*i)->id_res_hook(p,sp,identity); | 10 | for(iterator i=begin();i!=end();++i) (*i)->id_res_hook(om,sp); |
11 | } | 11 | } |
12 | void extension_chain_t::checkid_hook(const params_t& pin,params_t& pout) { | 12 | void extension_chain_t::checkid_hook(const basic_openid_message& inm,basic_openid_message& oum) { |
13 | for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(pin,pout); | 13 | for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(inm,oum); |
14 | } | 14 | } |
15 | 15 | ||
16 | } | 16 | } |
diff --git a/lib/openid_message.cc b/lib/openid_message.cc new file mode 100644 index 0000000..3b08748 --- a/dev/null +++ b/lib/openid_message.cc | |||
@@ -0,0 +1,228 @@ | |||
1 | #include <cassert> | ||
2 | #include <opkele/types.h> | ||
3 | #include <opkele/exception.h> | ||
4 | #include <opkele/util.h> | ||
5 | #include <opkele/debug.h> | ||
6 | |||
7 | #include "config.h" | ||
8 | |||
9 | namespace opkele { | ||
10 | using std::input_iterator_tag; | ||
11 | using std::unary_function; | ||
12 | |||
13 | struct __om_copier : public unary_function<const string&,void> { | ||
14 | public: | ||
15 | const basic_openid_message& from; | ||
16 | basic_openid_message& to; | ||
17 | |||
18 | __om_copier(basic_openid_message& to,const basic_openid_message& from) | ||
19 | : from(from), to(to) { | ||
20 | to.reset_fields(); | ||
21 | } | ||
22 | |||
23 | result_type operator()(argument_type f) { | ||
24 | to.set_field(f,from.get_field(f)); } | ||
25 | }; | ||
26 | |||
27 | basic_openid_message::basic_openid_message(const basic_openid_message& x) { | ||
28 | x.copy_to(*this); | ||
29 | } | ||
30 | void basic_openid_message::copy_to(basic_openid_message& x) const { | ||
31 | for_each(fields_begin(),fields_end(), | ||
32 | __om_copier(x,*this) ); | ||
33 | } | ||
34 | |||
35 | struct __om_ns_finder : public unary_function<const string&,bool> { | ||
36 | public: | ||
37 | const basic_openid_message& om; | ||
38 | const string& uri; | ||
39 | |||
40 | __om_ns_finder(const basic_openid_message& om, | ||
41 | const string& uri) : om(om), uri(uri) { } | ||
42 | |||
43 | result_type operator()(argument_type f) { | ||
44 | return | ||
45 | (!strncmp(f.c_str(),"ns.",sizeof("ns.")-1)) | ||
46 | && om.get_field(f)==uri ; | ||
47 | } | ||
48 | }; | ||
49 | |||
50 | bool basic_openid_message::has_ns(const string& uri) const { | ||
51 | fields_iterator ei = fields_end(); | ||
52 | fields_iterator i = find_if(fields_begin(),fields_end(), | ||
53 | __om_ns_finder(*this,uri)); | ||
54 | return !(i==ei); | ||
55 | } | ||
56 | string basic_openid_message::get_ns(const string& uri) const { | ||
57 | fields_iterator ei = fields_end(); | ||
58 | fields_iterator i = find_if(fields_begin(),fields_end(), | ||
59 | __om_ns_finder(*this,uri)); | ||
60 | if(i==ei) | ||
61 | throw failed_lookup(OPKELE_CP_ string("failed to find namespace ")+uri); | ||
62 | return i->substr(3); | ||
63 | } | ||
64 | |||
65 | struct __om_query_builder : public unary_function<const string&,void> { | ||
66 | public: | ||
67 | const basic_openid_message& om; | ||
68 | string& rv; | ||
69 | bool first; | ||
70 | |||
71 | __om_query_builder(string& rv,const basic_openid_message& om) | ||
72 | : om(om), first(true), rv(rv) { | ||
73 | for_each(om.fields_begin(),om.fields_end(),*this); | ||
74 | } | ||
75 | __om_query_builder(string& rv,const basic_openid_message& om,const string& url) | ||
76 | : om(om), first(true), rv(rv) { | ||
77 | rv = url; | ||
78 | if(rv.find('?')==string::npos) | ||
79 | rv += '?'; | ||
80 | else | ||
81 | first = false; | ||
82 | for_each(om.fields_begin(),om.fields_end(),*this); | ||
83 | } | ||
84 | |||
85 | result_type operator()(argument_type f) { | ||
86 | if(first) | ||
87 | first = false; | ||
88 | else | ||
89 | rv += '&'; | ||
90 | rv += "openid."; rv+= f; | ||
91 | rv += '='; | ||
92 | rv += util::url_encode(om.get_field(f)); | ||
93 | } | ||
94 | }; | ||
95 | |||
96 | string basic_openid_message::append_query(const string& url) const { | ||
97 | string rv; | ||
98 | return __om_query_builder(rv,*this,url).rv; | ||
99 | } | ||
100 | string basic_openid_message::query_string() const { | ||
101 | string rv; | ||
102 | return __om_query_builder(rv,*this).rv; | ||
103 | } | ||
104 | |||
105 | void basic_openid_message::reset_fields() { | ||
106 | throw not_implemented(OPKELE_CP_ "reset_fields() not implemented"); | ||
107 | } | ||
108 | void basic_openid_message::set_field(const string& n,const string& v) { | ||
109 | throw not_implemented(OPKELE_CP_ "set_field() not implemented"); | ||
110 | } | ||
111 | void basic_openid_message::reset_field(const string& n) { | ||
112 | throw not_implemented(OPKELE_CP_ "reset_field() not implemented"); | ||
113 | } | ||
114 | |||
115 | void basic_openid_message::from_keyvalues(const string& kv) { | ||
116 | reset_fields(); | ||
117 | string::size_type p = 0; | ||
118 | while(true) { | ||
119 | string::size_type co = kv.find(':',p); | ||
120 | if(co==string::npos) | ||
121 | break; | ||
122 | #ifndef POSTELS_LAW | ||
123 | string::size_type nl = kv.find('\n',co+1); | ||
124 | if(nl==string::npos) | ||
125 | throw bad_input(OPKELE_CP_ "malformed input"); | ||
126 | if(nl>co) | ||
127 | insert(value_type(kv.substr(p,co-p),kv.substr(co+1,nl-co-1))); | ||
128 | p = nl+1; | ||
129 | #else /* POSTELS_LAW */ | ||
130 | string::size_type lb = kv.find_first_of("\r\n",co+1); | ||
131 | if(lb==string::npos) { | ||
132 | set_field(kv.substr(p,co-p),kv.substr(co+1)); | ||
133 | break; | ||
134 | } | ||
135 | if(lb>co) | ||
136 | set_field(kv.substr(p,co-p),kv.substr(co+1,lb-co-1)); | ||
137 | string::size_type nolb = kv.find_first_not_of("\r\n",lb); | ||
138 | if(nolb==string::npos) | ||
139 | break; | ||
140 | p = nolb; | ||
141 | #endif /* POSTELS_LAW */ | ||
142 | } | ||
143 | } | ||
144 | |||
145 | void basic_openid_message::add_to_signed(const string& fields) { | ||
146 | string::size_type fnc = fields.find_first_not_of(","); | ||
147 | if(fnc==string::npos) | ||
148 | throw bad_input(OPKELE_CP_ "Trying to add nothing in particular to the list of signed fields"); | ||
149 | string signeds; | ||
150 | try { | ||
151 | signeds = get_field("signed"); | ||
152 | string::size_type lnc = signeds.find_last_not_of(","); | ||
153 | if(lnc==string::npos) | ||
154 | signeds.assign(fields,fnc,fields.size()-fnc); | ||
155 | else{ | ||
156 | string::size_type ss = signeds.size(); | ||
157 | if(lnc==(ss-1)) { | ||
158 | signeds+= ','; | ||
159 | signeds.append(fields,fnc,fields.size()-fnc); | ||
160 | }else{ | ||
161 | if(lnc<(ss-2)) | ||
162 | signeds.replace(lnc+2,ss-lnc-2, | ||
163 | fields,fnc,fields.size()-fnc); | ||
164 | else | ||
165 | signeds.append(fields,fnc,fields.size()-fnc); | ||
166 | } | ||
167 | } | ||
168 | }catch(failed_lookup&) { | ||
169 | signeds.assign(fields,fnc,fields.size()-fnc); | ||
170 | } | ||
171 | set_field("signed",signeds); | ||
172 | } | ||
173 | |||
174 | string basic_openid_message::find_ns(const string& uri,const char *pfx) const { | ||
175 | if(has_field("ns")) | ||
176 | return get_ns(uri); | ||
177 | return pfx; | ||
178 | } | ||
179 | string basic_openid_message::allocate_ns(const string& uri,const char *pfx) { | ||
180 | if(!has_field("ns")) | ||
181 | return pfx; | ||
182 | if(has_ns(uri)) | ||
183 | throw bad_input(OPKELE_CP_ "OpenID message already contains namespace"); | ||
184 | string rv = pfx; | ||
185 | if(has_field("ns."+rv)) { | ||
186 | string::reference c=rv[rv.length()]; | ||
187 | for(c='a';c<='z' && has_field("ns."+rv);++c); | ||
188 | if(c=='z') | ||
189 | throw exception(OPKELE_CP_ "Failed to allocate namespace"); | ||
190 | } | ||
191 | set_field("ns."+rv,uri); | ||
192 | return rv; | ||
193 | } | ||
194 | |||
195 | void openid_message_t::copy_to(basic_openid_message& x) const { | ||
196 | x.reset_fields(); | ||
197 | for(const_iterator i=begin();i!=end();++i) | ||
198 | x.set_field(i->first,i->second); | ||
199 | } | ||
200 | |||
201 | bool openid_message_t::has_field(const string& n) const { | ||
202 | return find(n)!=end(); | ||
203 | } | ||
204 | const string& openid_message_t::get_field(const string& n) const { | ||
205 | const_iterator i=find(n); | ||
206 | if(i==end()) | ||
207 | throw failed_lookup(OPKELE_CP_ n+": no such field"); | ||
208 | return i->second; | ||
209 | } | ||
210 | |||
211 | openid_message_t::fields_iterator openid_message_t::fields_begin() const { | ||
212 | return util::map_keys_iterator<const_iterator,string,const string&,const string*>(begin(),end()); | ||
213 | } | ||
214 | openid_message_t::fields_iterator openid_message_t::fields_end() const { | ||
215 | return util::map_keys_iterator<const_iterator,string,const string&,const string*>(end(),end()); | ||
216 | } | ||
217 | |||
218 | void openid_message_t::reset_fields() { | ||
219 | clear(); | ||
220 | } | ||
221 | void openid_message_t::set_field(const string& n,const string& v) { | ||
222 | insert(value_type(n,v)); | ||
223 | } | ||
224 | void openid_message_t::reset_field(const string& n) { | ||
225 | erase(n); | ||
226 | } | ||
227 | |||
228 | } | ||
diff --git a/lib/params.cc b/lib/params.cc index 7a572c1..6805516 100644 --- a/lib/params.cc +++ b/lib/params.cc | |||
@@ -6,116 +6,25 @@ | |||
6 | 6 | ||
7 | #include "config.h" | 7 | #include "config.h" |
8 | 8 | ||
9 | namespace opkele { | 9 | namespace opkele { |
10 | using namespace std; | 10 | using namespace std; |
11 | 11 | ||
12 | bool params_t::has_param(const string& n) const { | ||
13 | return find(n)!=end(); | ||
14 | } | ||
15 | const string& params_t::get_param(const string& n) const { | ||
16 | const_iterator i = find(n); | ||
17 | if(i==end()) | ||
18 | throw failed_lookup(OPKELE_CP_ n+": no such parameter"); | ||
19 | return i->second; | ||
20 | } | ||
21 | string& params_t::get_param(const string& n) { | ||
22 | iterator i = find(n); | ||
23 | if(i==end()) | ||
24 | throw failed_lookup(OPKELE_CP_ n+": no such parameter"); | ||
25 | return i->second; | ||
26 | } | ||
27 | |||
28 | void params_t::parse_keyvalues(const string& kv) { | ||
29 | clear(); | ||
30 | string::size_type p = 0; | ||
31 | while(true) { | ||
32 | string::size_type co = kv.find(':',p); | ||
33 | if(co==string::npos) | ||
34 | break; | ||
35 | #ifndef POSTELS_LAW | ||
36 | string::size_type nl = kv.find('\n',co+1); | ||
37 | if(nl==string::npos) | ||
38 | throw bad_input(OPKELE_CP_ "malformed input"); | ||
39 | if(nl>co) | ||
40 | insert(value_type(kv.substr(p,co-p),kv.substr(co+1,nl-co-1))); | ||
41 | p = nl+1; | ||
42 | #else /* POSTELS_LAW */ | ||
43 | string::size_type lb = kv.find_first_of("\r\n",co+1); | ||
44 | if(lb==string::npos) { | ||
45 | insert(value_type(kv.substr(p,co-p),kv.substr(co+1))); | ||
46 | break; | ||
47 | } | ||
48 | if(lb>co) | ||
49 | insert(value_type(kv.substr(p,co-p),kv.substr(co+1,lb-co-1))); | ||
50 | string::size_type nolb = kv.find_first_not_of("\r\n",lb); | ||
51 | if(nolb==string::npos) | ||
52 | break; | ||
53 | p = nolb; | ||
54 | #endif /* POSTELS_LAW */ | ||
55 | } | ||
56 | } | ||
57 | |||
58 | void params_t::sign(secret_t secret,string& sig,const string& slist,const char *prefix) const { | ||
59 | string kv; | ||
60 | string::size_type p = 0; | ||
61 | while(true) { | ||
62 | string::size_type co = slist.find(',',p); | ||
63 | string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p); | ||
64 | kv += f; | ||
65 | kv += ':'; | ||
66 | if(prefix) f.insert(0,prefix); | ||
67 | kv += get_param(f); | ||
68 | kv += '\n'; | ||
69 | if(co==string::npos) | ||
70 | break; | ||
71 | p = co+1; | ||
72 | } | ||
73 | unsigned int md_len = 0; | ||
74 | unsigned char *md = HMAC( | ||
75 | EVP_sha1(), | ||
76 | &(secret.front()),secret.size(), | ||
77 | (const unsigned char *)kv.data(),kv.length(), | ||
78 | 0,&md_len); | ||
79 | sig = util::encode_base64(md,md_len); | ||
80 | } | ||
81 | |||
82 | string params_t::append_query(const string& url,const char *prefix) const { | 12 | string params_t::append_query(const string& url,const char *prefix) const { |
83 | string rv = url; | 13 | string rv = url; |
84 | bool p = true; | 14 | bool p = true; |
85 | if(rv.find('?')==string::npos) { | 15 | if(rv.find('?')==string::npos) { |
86 | rv += '?'; | 16 | rv += '?'; p = false; } |
87 | p = false; | 17 | for(fields_iterator i=fields_begin();i!=fields_end();++i) { |
88 | } | ||
89 | for(const_iterator i=begin();i!=end();++i) { | ||
90 | if(p) | 18 | if(p) |
91 | rv += '&'; | 19 | rv += '&'; |
92 | else | 20 | else |
93 | p = true; | 21 | p = true; |
94 | rv += prefix; | 22 | if(prefix) rv += prefix; |
95 | rv += i->first; | 23 | rv += *i; |
96 | rv += '='; | 24 | rv += '='; |
97 | rv += util::url_encode(i->second); | 25 | rv += util::url_encode(get_field(*i)); |
98 | } | 26 | } |
99 | return rv; | 27 | return rv; |
100 | } | 28 | } |
101 | 29 | ||
102 | string params_t::query_string(const char *prefix) const { | ||
103 | string rv; | ||
104 | for(const_iterator i=begin();i!=end();++i) { | ||
105 | if(!rv.empty()) | ||
106 | rv += '&'; | ||
107 | rv += prefix; | ||
108 | rv += i->first; | ||
109 | rv += '='; | ||
110 | rv += util::url_encode(i->second); | ||
111 | } | ||
112 | return rv; | ||
113 | } | ||
114 | |||
115 | ostream& operator << (ostream& o,const params_t& p) { | ||
116 | for(params_t::const_iterator i=p.begin();i!=p.end();++i) | ||
117 | o << i->first << ':' << i->second << '\n'; | ||
118 | return o; | ||
119 | } | ||
120 | |||
121 | } | 30 | } |
diff --git a/lib/prequeue_rp.cc b/lib/prequeue_rp.cc new file mode 100644 index 0000000..e242f87 --- a/dev/null +++ b/lib/prequeue_rp.cc | |||
@@ -0,0 +1,81 @@ | |||
1 | #include <iostream> | ||
2 | #include <openssl/sha.h> | ||
3 | #include <openssl/hmac.h> | ||
4 | #include <opkele/exception.h> | ||
5 | #include <opkele/prequeue_rp.h> | ||
6 | #include <opkele/discovery.h> | ||
7 | #include <opkele/uris.h> | ||
8 | #include <opkele/data.h> | ||
9 | #include <opkele/util.h> | ||
10 | #include <opkele/curl.h> | ||
11 | #include <opkele/debug.h> | ||
12 | |||
13 | namespace opkele { | ||
14 | |||
15 | class __OP_verifier_good_input : public exception { | ||
16 | public: | ||
17 | __OP_verifier_good_input(OPKELE_E_PARS) | ||
18 | : exception(OPKELE_E_CONS) { } | ||
19 | }; | ||
20 | |||
21 | class OP_verifier : public iterator<output_iterator_tag,openid_endpoint_t,void> { | ||
22 | public: | ||
23 | const string& OP; | ||
24 | const string& id; | ||
25 | |||
26 | OP_verifier(const string& o,const string& i) | ||
27 | : OP(o), id(i) { } | ||
28 | |||
29 | OP_verifier& operator*() { return *this; } | ||
30 | OP_verifier& operator=(const openid_endpoint_t& oep) { | ||
31 | if(oep.uri==OP) { | ||
32 | if(oep.claimed_id==IDURI_SELECT20 | ||
33 | || oep.local_id==IDURI_SELECT20 ) | ||
34 | throw bad_input(OPKELE_CP_ "claimed_id is an OP-Id"); | ||
35 | if(oep.local_id==id) | ||
36 | throw __OP_verifier_good_input(OPKELE_CP_ "Found corresponding endpoint"); | ||
37 | } | ||
38 | return *this; | ||
39 | } | ||
40 | |||
41 | OP_verifier& operator++() { return *this; } | ||
42 | OP_verifier& operator++(int) { return *this; } | ||
43 | }; | ||
44 | |||
45 | void prequeue_RP::verify_OP(const string& OP,const string& claimed_id,const string& identity) const { | ||
46 | try { | ||
47 | idiscover(OP_verifier(OP,identity),claimed_id); | ||
48 | throw id_res_unauthorized(OPKELE_CP_ | ||
49 | "OP is not authorized to make an assertion regarding the identity"); | ||
50 | }catch(__OP_verifier_good_input& ovgi) { | ||
51 | } | ||
52 | } | ||
53 | |||
54 | class endpoint_queuer : public iterator<output_iterator_tag,openid_endpoint_t,void> { | ||
55 | public: | ||
56 | prequeue_RP& rp; | ||
57 | |||
58 | endpoint_queuer(prequeue_RP& rp) : rp(rp) { } | ||
59 | |||
60 | endpoint_queuer& operator*() { return *this; } | ||
61 | endpoint_queuer& operator=(const openid_endpoint_t& oep) { | ||
62 | rp.queue_endpoint(oep); return *this; } | ||
63 | |||
64 | endpoint_queuer& operator++() { return *this; } | ||
65 | endpoint_queuer& operator++(int) { return *this; } | ||
66 | }; | ||
67 | |||
68 | void prequeue_RP::initiate(const string& usi) { | ||
69 | begin_queueing(); | ||
70 | set_normalized_id( idiscover(endpoint_queuer(*this),usi) ); | ||
71 | end_queueing(); | ||
72 | } | ||
73 | |||
74 | void prequeue_RP::set_normalized_id(const string& nid) { | ||
75 | } | ||
76 | |||
77 | const string prequeue_RP::get_normalized_id() const { | ||
78 | throw not_implemented(OPKELE_CP_ "get_normalized_id() is not implemented"); | ||
79 | } | ||
80 | |||
81 | } | ||
diff --git a/lib/server.cc b/lib/server.cc index 282521e..776f1ae 100644 --- a/lib/server.cc +++ b/lib/server.cc | |||
@@ -106,13 +106,13 @@ namespace opkele { | |||
106 | time_t now = time(0); | 106 | time_t now = time(0); |
107 | pout["issued"] = util::time_to_w3c(now); | 107 | pout["issued"] = util::time_to_w3c(now); |
108 | pout["valid_to"] = util::time_to_w3c(now+120); | 108 | pout["valid_to"] = util::time_to_w3c(now+120); |
109 | pout["exipres_in"] = "120"; | 109 | pout["exipres_in"] = "120"; |
110 | pout["signed"]="mode,identity,return_to"; | 110 | pout["signed"]="mode,identity,return_to"; |
111 | if(ext) ext->checkid_hook(pin,pout); | 111 | if(ext) ext->checkid_hook(pin,pout); |
112 | pout.sign(assoc->secret(),pout["sig"],pout["signed"]); | 112 | pout["sig"] = util::base64_signature(assoc,pout); |
113 | } | 113 | } |
114 | 114 | ||
115 | void server_t::check_authentication(const params_t& pin,params_t& pout) { | 115 | void server_t::check_authentication(const params_t& pin,params_t& pout) { |
116 | vector<unsigned char> sig; | 116 | vector<unsigned char> sig; |
117 | const string& sigenc = pin.get_param("openid.sig"); | 117 | const string& sigenc = pin.get_param("openid.sig"); |
118 | util::decode_base64(sigenc,sig); | 118 | util::decode_base64(sigenc,sig); |
diff --git a/lib/sreg.cc b/lib/sreg.cc index 03edf57..7e2d588 100644 --- a/lib/sreg.cc +++ b/lib/sreg.cc | |||
@@ -25,37 +25,48 @@ namespace opkele { | |||
25 | typedef const struct _sreg_field *fields_iterator; | 25 | typedef const struct _sreg_field *fields_iterator; |
26 | 26 | ||
27 | bool operator==(const struct _sreg_field& fd,const string& fn) { | 27 | bool operator==(const struct _sreg_field& fd,const string& fn) { |
28 | return fd.fieldname==fn; | 28 | return fd.fieldname==fn; |
29 | } | 29 | } |
30 | 30 | ||
31 | void sreg_t::checkid_hook(params_t& p,const string& /* identity */) { | 31 | void sreg_t::checkid_hook(basic_openid_message& om) { |
32 | string fr, fo; | 32 | string fr, fo; |
33 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { | 33 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { |
34 | if(f->fieldbit&fields_required) { | 34 | if(f->fieldbit&fields_required) { |
35 | if(!fr.empty()) fr+=","; | 35 | if(!fr.empty()) fr+=","; |
36 | fr += f->fieldname; | 36 | fr += f->fieldname; |
37 | } | 37 | } |
38 | if(f->fieldbit&fields_optional) { | 38 | if(f->fieldbit&fields_optional) { |
39 | if(!fo.empty()) fo+=","; | 39 | if(!fo.empty()) fo+=","; |
40 | fo += f->fieldname; | 40 | fo += f->fieldname; |
41 | } | 41 | } |
42 | } | 42 | } |
43 | p["ns.sreg"] = OIURI_SREG11; | 43 | string pfx = om.allocate_ns(OIURI_SREG11,"sreg"); |
44 | if(!fr.empty()) p["sreg.required"]=fr; | 44 | if(!fr.empty()) om.set_field(pfx+".required",fr); |
45 | if(!fo.empty()) p["sreg.optional"]=fo; | 45 | if(!fo.empty()) om.set_field(pfx+".optional",fo); |
46 | if(!policy_url.empty()) p["sreg.policy_url"]=policy_url; | 46 | if(!policy_url.empty()) om.set_field(pfx+".policy_url",policy_url); |
47 | } | 47 | } |
48 | 48 | ||
49 | void sreg_t::id_res_hook(const params_t& /* p */,const params_t& sp,const string& /* identity */) { | 49 | void sreg_t::id_res_hook(const basic_openid_message& om,const basic_openid_message& sp) { |
50 | clear(); | 50 | clear(); |
51 | string pfx; | ||
52 | try { | ||
53 | pfx = om.find_ns(OIURI_SREG11,"sreg"); | ||
54 | }catch(failed_lookup& fl) { | ||
55 | try { | ||
56 | pfx = om.find_ns(OIURI_SREG10,"sreg"); | ||
57 | }catch(failed_lookup& fl) { | ||
58 | return; | ||
59 | } | ||
60 | } | ||
61 | pfx += '.'; | ||
51 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { | 62 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { |
52 | string fn = "sreg."; fn+=f->fieldname; | 63 | string fn = pfx; fn+=f->fieldname; |
53 | if(!sp.has_param(fn)) continue; | 64 | if(!sp.has_field(fn)) continue; |
54 | has_fields |= f->fieldbit; | 65 | has_fields |= f->fieldbit; |
55 | response[f->fieldbit]=sp.get_param(fn); | 66 | response[f->fieldbit]=sp.get_field(fn); |
56 | } | 67 | } |
57 | } | 68 | } |
58 | 69 | ||
59 | const string& sreg_t::get_field(fieldbit_t fb) const { | 70 | const string& sreg_t::get_field(fieldbit_t fb) const { |
60 | response_t::const_iterator i = response.find(fb); | 71 | response_t::const_iterator i = response.find(fb); |
61 | if(i==response.end()) | 72 | if(i==response.end()) |
@@ -91,36 +102,39 @@ namespace opkele { | |||
91 | if(f!=fields_END) | 102 | if(f!=fields_END) |
92 | rv |= f->fieldbit; | 103 | rv |= f->fieldbit; |
93 | } | 104 | } |
94 | return rv; | 105 | return rv; |
95 | } | 106 | } |
96 | 107 | ||
97 | void sreg_t::checkid_hook(const params_t& pin,params_t& pout) { | 108 | void sreg_t::checkid_hook(const basic_openid_message& inm,basic_openid_message& oum) { |
109 | string ins = inm.find_ns(OIURI_SREG11,"sreg"); | ||
98 | fields_optional = 0; fields_required = 0; policy_url.erase(); | 110 | fields_optional = 0; fields_required = 0; policy_url.erase(); |
99 | fields_response = 0; | 111 | fields_response = 0; |
100 | try { | 112 | try { |
101 | string fl = pin.get_param("openid.sreg.required"); | 113 | string fl = inm.get_field(ins+".required"); |
102 | fields_required = fields_list_to_bitmask(fl); | 114 | fields_required = fields_list_to_bitmask(fl); |
103 | }catch(failed_lookup&) { } | 115 | }catch(failed_lookup&) { } |
104 | try { | 116 | try { |
105 | string fl = pin.get_param("openid.sreg.optional"); | 117 | string fl = inm.get_field(ins+".optional"); |
106 | fields_optional = fields_list_to_bitmask(fl); | 118 | fields_optional = fields_list_to_bitmask(fl); |
107 | }catch(failed_lookup&) { } | 119 | }catch(failed_lookup&) { } |
108 | try { | 120 | try { |
109 | policy_url = pin.get_param("openid.sreg.policy_url"); | 121 | policy_url = inm.get_field(ins+".policy_url"); |
110 | }catch(failed_lookup&) { } | 122 | }catch(failed_lookup&) { } |
111 | setup_response(pin,pout); | 123 | setup_response(inm,oum); |
124 | string ons = oum.allocate_ns(OIURI_SREG11,"sreg"); | ||
112 | fields_response &= has_fields; | 125 | fields_response &= has_fields; |
126 | string signeds = "ns."+ons; | ||
113 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { | 127 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { |
114 | if(!(f->fieldbit&fields_response)) continue; | 128 | if(!(f->fieldbit&fields_response)) continue; |
115 | if(!pout["signed"].empty()) | 129 | signeds +=','; |
116 | pout["signed"] +=','; | 130 | string pn = ons; pn += '.'; pn += f->fieldname; |
117 | string pn = "sreg."; pn += f->fieldname; | 131 | signeds += pn; |
118 | pout["signed"] += pn; | 132 | oum.set_field(pn,get_field(f->fieldbit)); |
119 | pout[pn] = get_field(f->fieldbit); | ||
120 | } | 133 | } |
134 | oum.add_to_signed(signeds); | ||
121 | } | 135 | } |
122 | 136 | ||
123 | void sreg_t::setup_response(const params_t& /* pin */,params_t& /* pout */) { | 137 | void sreg_t::setup_response(const basic_openid_message& /* inm */,basic_openid_message& /* oum */) { |
124 | fields_response = (fields_required|fields_optional)&has_fields; | 138 | fields_response = (fields_required|fields_optional)&has_fields; |
125 | } | 139 | } |
126 | } | 140 | } |
diff --git a/lib/util.cc b/lib/util.cc index a9b9bed..54d6535 100644 --- a/lib/util.cc +++ b/lib/util.cc | |||
@@ -4,16 +4,22 @@ | |||
4 | #include <cstring> | 4 | #include <cstring> |
5 | #include <vector> | 5 | #include <vector> |
6 | #include <string> | 6 | #include <string> |
7 | #include <stack> | 7 | #include <stack> |
8 | #include <openssl/bio.h> | 8 | #include <openssl/bio.h> |
9 | #include <openssl/evp.h> | 9 | #include <openssl/evp.h> |
10 | #include <openssl/hmac.h> | ||
10 | #include <curl/curl.h> | 11 | #include <curl/curl.h> |
11 | #include "opkele/util.h" | 12 | #include "opkele/util.h" |
12 | #include "opkele/exception.h" | 13 | #include "opkele/exception.h" |
13 | 14 | ||
15 | #include <config.h> | ||
16 | #ifdef HAVE_DEMANGLE | ||
17 | # include <cxxabi.h> | ||
18 | #endif | ||
19 | |||
14 | namespace opkele { | 20 | namespace opkele { |
15 | using namespace std; | 21 | using namespace std; |
16 | 22 | ||
17 | namespace util { | 23 | namespace util { |
18 | 24 | ||
19 | /* | 25 | /* |
@@ -202,14 +208,13 @@ namespace opkele { | |||
202 | throw bad_input(OPKELE_CP_ "Unexpected input in URI being normalized after scheme component"); | 208 | throw bad_input(OPKELE_CP_ "Unexpected input in URI being normalized after scheme component"); |
203 | if(rv=="http:") | 209 | if(rv=="http:") |
204 | s = false; | 210 | s = false; |
205 | else if(rv=="https:") | 211 | else if(rv=="https:") |
206 | s = true; | 212 | s = true; |
207 | else{ | 213 | else{ |
208 | /* TODO: support more schemes. | 214 | /* TODO: support more schemes. e.g. xri. How do we normalize |
209 | * e.g. xri. How do we normalize | ||
210 | * xri? | 215 | * xri? |
211 | */ | 216 | */ |
212 | rv.append(uri,colon+1,ul-colon-1); | 217 | rv.append(uri,colon+1,ul-colon-1); |
213 | return rv; | 218 | return rv; |
214 | } | 219 | } |
215 | rv += "//"; | 220 | rv += "//"; |
@@ -308,9 +313,71 @@ namespace opkele { | |||
308 | if(!qf) rv += '/'; | 313 | if(!qf) rv += '/'; |
309 | rv += pseg; | 314 | rv += pseg; |
310 | } | 315 | } |
311 | return rv; | 316 | return rv; |
312 | } | 317 | } |
313 | 318 | ||
319 | string& strip_uri_fragment_part(string& u) { | ||
320 | string::size_type q = u.find('?'), f = u.find('#'); | ||
321 | if(q==string::npos) { | ||
322 | if(f!=string::npos) | ||
323 | u.erase(f); | ||
324 | }else{ | ||
325 | if(f!=string::npos) { | ||
326 | if(f<q) | ||
327 | u.erase(f,q-f); | ||
328 | else | ||
329 | u.erase(f); | ||
330 | } | ||
331 | } | ||
332 | return u; | ||
333 | } | ||
334 | |||
335 | string abi_demangle(const char *mn) { | ||
336 | #ifndef HAVE_DEMANGLE | ||
337 | return mn; | ||
338 | #else /* !HAVE_DEMANGLE */ | ||
339 | int dstat; | ||
340 | char *demangled = abi::__cxa_demangle(mn,0,0,&dstat); | ||
341 | if(dstat) | ||
342 | return mn; | ||
343 | string rv = demangled; | ||
344 | free(demangled); | ||
345 | return rv; | ||
346 | #endif /* !HAVE_DEMANGLE */ | ||
347 | } | ||
348 | |||
349 | string base64_signature(const assoc_t& assoc,const basic_openid_message& om) { | ||
350 | const string& slist = om.get_field("signed"); | ||
351 | string kv; | ||
352 | string::size_type p=0; | ||
353 | while(true) { | ||
354 | string::size_type co = slist.find(',',p); | ||
355 | string f = (co==string::npos) | ||
356 | ?slist.substr(p):slist.substr(p,co-p); | ||
357 | kv += f; | ||
358 | kv += ':'; | ||
359 | kv += om.get_field(f); | ||
360 | kv += '\n'; | ||
361 | if(co==string::npos) break; | ||
362 | p = co+1; | ||
363 | } | ||
364 | const secret_t& secret = assoc->secret(); | ||
365 | const EVP_MD *evpmd; | ||
366 | const string& at = assoc->assoc_type(); | ||
367 | if(at=="HMAC-SHA256") | ||
368 | evpmd = EVP_sha256(); | ||
369 | else if(at=="HMAC-SHA1") | ||
370 | evpmd = EVP_sha1(); | ||
371 | else | ||
372 | throw unsupported(OPKELE_CP_ "unknown association type"); | ||
373 | unsigned int md_len = 0; | ||
374 | unsigned char *md = HMAC(evpmd, | ||
375 | &(secret.front()),secret.size(), | ||
376 | (const unsigned char*)kv.data(),kv.length(), | ||
377 | 0,&md_len); | ||
378 | return encode_base64(md,md_len); | ||
379 | } | ||
380 | |||
314 | } | 381 | } |
315 | 382 | ||
316 | } | 383 | } |