author | Michael Krelin <hacker@klever.net> | 2008-02-19 10:52:09 (UTC) |
---|---|---|
committer | Michael Krelin <hacker@klever.net> | 2008-02-19 10:52:09 (UTC) |
commit | 42e4fb613d190508b3e8b8993d233044eeea4d20 (patch) (unidiff) | |
tree | 9b8ebc420942554f927a777e03c70a7c65305a88 | |
parent | a3db32747e8370cab8cfdcc382fee875613b7b77 (diff) | |
download | libopkele-42e4fb613d190508b3e8b8993d233044eeea4d20.zip libopkele-42e4fb613d190508b3e8b8993d233044eeea4d20.tar.gz libopkele-42e4fb613d190508b3e8b8993d233044eeea4d20.tar.bz2 |
basic_RP: add methods for accessing identity information passed from OP.
Signed-off-by: Michael Krelin <hacker@klever.net>
-rw-r--r-- | include/opkele/basic_rp.h | 36 | ||||
-rw-r--r-- | lib/basic_rp.cc | 29 |
2 files changed, 63 insertions, 2 deletions
diff --git a/include/opkele/basic_rp.h b/include/opkele/basic_rp.h index d5356aa..d096e0a 100644 --- a/include/opkele/basic_rp.h +++ b/include/opkele/basic_rp.h | |||
@@ -1,200 +1,236 @@ | |||
1 | #ifndef __OPKELE_BASIC_RP_H | 1 | #ifndef __OPKELE_BASIC_RP_H |
2 | #define __OPKELE_BASIC_RP_H | 2 | #define __OPKELE_BASIC_RP_H |
3 | 3 | ||
4 | #include <string> | 4 | #include <string> |
5 | #include <opkele/types.h> | 5 | #include <opkele/types.h> |
6 | #include <opkele/extension.h> | 6 | #include <opkele/extension.h> |
7 | 7 | ||
8 | namespace opkele { | 8 | namespace opkele { |
9 | using std::string; | 9 | using std::string; |
10 | 10 | ||
11 | class basic_RP { | 11 | class basic_RP { |
12 | public: | 12 | public: |
13 | /** | ||
14 | * Claimed identifier from a parsed id_res message. | ||
15 | */ | ||
16 | string claimed_id; | ||
17 | /** | ||
18 | * OP-Local identifier from a parsed id_res message. | ||
19 | */ | ||
20 | string identity; | ||
13 | 21 | ||
14 | virtual ~basic_RP() { } | 22 | virtual ~basic_RP() { } |
15 | 23 | ||
24 | void reset_vars(); | ||
25 | |||
26 | /** | ||
27 | * @name Assertion information retrieval | ||
28 | * Retrieval of the information passed with openid message | ||
29 | * @{ | ||
30 | */ | ||
31 | /** | ||
32 | * Find out if the assertion is about identity | ||
33 | * @return true if so | ||
34 | */ | ||
35 | bool has_identity() const; | ||
36 | /** | ||
37 | * Get claimed identifier supplied with the request | ||
38 | * @return claimed identifier | ||
39 | * @throw non_identity if request is not about identity | ||
40 | */ | ||
41 | const string& get_claimed_id() const; | ||
42 | /** | ||
43 | * Get the identity (OP-Local identifier) confirmed | ||
44 | * @return identity | ||
45 | * @throw non_identity if request is not about identity | ||
46 | */ | ||
47 | const string& get_identity() const; | ||
48 | /** | ||
49 | * @} | ||
50 | */ | ||
51 | |||
16 | /** | 52 | /** |
17 | * @name Global persistent store API | 53 | * @name Global persistent store API |
18 | * These are functions related to the associations with OP storage | 54 | * These are functions related to the associations with OP storage |
19 | * and retrieval and nonce records. They provide an interface to | 55 | * and retrieval and nonce records. They provide an interface to |
20 | * the persistent storage which is shared by all sessions. If the | 56 | * the persistent storage which is shared by all sessions. If the |
21 | * implementor prefers the dumb mode instead, the function should | 57 | * implementor prefers the dumb mode instead, the function should |
22 | * throw dumb_RP exception instead. | 58 | * throw dumb_RP exception instead. |
23 | * @see opkele::dumb_RP | 59 | * @see opkele::dumb_RP |
24 | * @{ | 60 | * @{ |
25 | */ | 61 | */ |
26 | /** | 62 | /** |
27 | * Store association and return allocated association object. | 63 | * Store association and return allocated association object. |
28 | * @param OP OP endpoint | 64 | * @param OP OP endpoint |
29 | * @param handle association handle | 65 | * @param handle association handle |
30 | * @param type association type | 66 | * @param type association type |
31 | * @param secret association secret | 67 | * @param secret association secret |
32 | * @params expires_in the number of seconds association expires in | 68 | * @params expires_in the number of seconds association expires in |
33 | * @return the association object | 69 | * @return the association object |
34 | * @throw dumb_RP for dumb RP | 70 | * @throw dumb_RP for dumb RP |
35 | */ | 71 | */ |
36 | virtual assoc_t store_assoc( | 72 | virtual assoc_t store_assoc( |
37 | const string& OP,const string& handle, | 73 | const string& OP,const string& handle, |
38 | const string& type,const secret_t& secret, | 74 | const string& type,const secret_t& secret, |
39 | int expires_in) = 0; | 75 | int expires_in) = 0; |
40 | /** | 76 | /** |
41 | * Find valid unexpired association with an OP. | 77 | * Find valid unexpired association with an OP. |
42 | * @param OP OP endpoint URL | 78 | * @param OP OP endpoint URL |
43 | * @return association found | 79 | * @return association found |
44 | * @throw failed_lookup if no association found | 80 | * @throw failed_lookup if no association found |
45 | * @throw dumb_RP for dumb RP | 81 | * @throw dumb_RP for dumb RP |
46 | */ | 82 | */ |
47 | virtual assoc_t find_assoc( | 83 | virtual assoc_t find_assoc( |
48 | const string& OP) = 0; | 84 | const string& OP) = 0; |
49 | /** | 85 | /** |
50 | * Retrieve valid association handle for an OP by handle. | 86 | * Retrieve valid association handle for an OP by handle. |
51 | * @param OP OP endpoint URL | 87 | * @param OP OP endpoint URL |
52 | * @param handle association handle | 88 | * @param handle association handle |
53 | * @return association found | 89 | * @return association found |
54 | * @throw failed_lookup if no association found | 90 | * @throw failed_lookup if no association found |
55 | * @throw dumb_RP for dumb RP | 91 | * @throw dumb_RP for dumb RP |
56 | */ | 92 | */ |
57 | virtual assoc_t retrieve_assoc( | 93 | virtual assoc_t retrieve_assoc( |
58 | const string& OP,const string& handle) = 0; | 94 | const string& OP,const string& handle) = 0; |
59 | /** | 95 | /** |
60 | * Invalidate association with OP | 96 | * Invalidate association with OP |
61 | * @param OP OP endpoint URL | 97 | * @param OP OP endpoint URL |
62 | * @param handle association handle | 98 | * @param handle association handle |
63 | * @throw dumb_RP for dumb RP | 99 | * @throw dumb_RP for dumb RP |
64 | */ | 100 | */ |
65 | virtual void invalidate_assoc(const string& OP,const string& handle) = 0; | 101 | virtual void invalidate_assoc(const string& OP,const string& handle) = 0; |
66 | 102 | ||
67 | /** | 103 | /** |
68 | * Check the nonce validity. That is, check that we haven't | 104 | * Check the nonce validity. That is, check that we haven't |
69 | * accepted request with this nonce from this OP, yet. May involve | 105 | * accepted request with this nonce from this OP, yet. May involve |
70 | * cutting off by the timestamp and checking the rest against the | 106 | * cutting off by the timestamp and checking the rest against the |
71 | * store of seen nonces. | 107 | * store of seen nonces. |
72 | * @param OP OP endpoint URL | 108 | * @param OP OP endpoint URL |
73 | * @param nonce nonce value | 109 | * @param nonce nonce value |
74 | * @throw id_res_bad_nonce if the nonce is not to be accepted, i.e. | 110 | * @throw id_res_bad_nonce if the nonce is not to be accepted, i.e. |
75 | * either too old or seen. | 111 | * either too old or seen. |
76 | */ | 112 | */ |
77 | virtual void check_nonce(const string& OP,const string& nonce) = 0; | 113 | virtual void check_nonce(const string& OP,const string& nonce) = 0; |
78 | /** | 114 | /** |
79 | * @} | 115 | * @} |
80 | */ | 116 | */ |
81 | 117 | ||
82 | /** | 118 | /** |
83 | * @name Session persistent store API | 119 | * @name Session persistent store API |
84 | * @{ | 120 | * @{ |
85 | */ | 121 | */ |
86 | /** | 122 | /** |
87 | * Retrieve OpenID endpoint being currently used for | 123 | * Retrieve OpenID endpoint being currently used for |
88 | * authentication. If there is no endpoint available, throw a | 124 | * authentication. If there is no endpoint available, throw a |
89 | * no_endpoint exception. | 125 | * no_endpoint exception. |
90 | * @return reference to the service endpoint object | 126 | * @return reference to the service endpoint object |
91 | * @see next_endpoint | 127 | * @see next_endpoint |
92 | * @throw no_endpoint if no endpoint available | 128 | * @throw no_endpoint if no endpoint available |
93 | */ | 129 | */ |
94 | virtual const openid_endpoint_t& get_endpoint() const = 0; | 130 | virtual const openid_endpoint_t& get_endpoint() const = 0; |
95 | /** | 131 | /** |
96 | * Advance to the next endpoint to try. | 132 | * Advance to the next endpoint to try. |
97 | * @see get_endpoint() | 133 | * @see get_endpoint() |
98 | * @throw no_endpoint if there are no more endpoints | 134 | * @throw no_endpoint if there are no more endpoints |
99 | */ | 135 | */ |
100 | virtual void next_endpoint() = 0; | 136 | virtual void next_endpoint() = 0; |
101 | /** | 137 | /** |
102 | * @} | 138 | * @} |
103 | */ | 139 | */ |
104 | 140 | ||
105 | /** | 141 | /** |
106 | * @name Site particulars API | 142 | * @name Site particulars API |
107 | * @{ | 143 | * @{ |
108 | */ | 144 | */ |
109 | /** | 145 | /** |
110 | * Return an absolute URL of the page being processed, includining | 146 | * Return an absolute URL of the page being processed, includining |
111 | * query parameters. It is used to validate return_to URL on | 147 | * query parameters. It is used to validate return_to URL on |
112 | * positive assertions. | 148 | * positive assertions. |
113 | * @return fully qualified url of the page being processed. | 149 | * @return fully qualified url of the page being processed. |
114 | */ | 150 | */ |
115 | virtual const string get_this_url() const = 0; | 151 | virtual const string get_this_url() const = 0; |
116 | /** | 152 | /** |
117 | * @} | 153 | * @} |
118 | */ | 154 | */ |
119 | 155 | ||
120 | /** | 156 | /** |
121 | * @name OpenID actions | 157 | * @name OpenID actions |
122 | * @{ | 158 | * @{ |
123 | */ | 159 | */ |
124 | /** | 160 | /** |
125 | * Initiates authentication session, doing discovery, normalization | 161 | * Initiates authentication session, doing discovery, normalization |
126 | * and whatever implementor wants to do at this point. | 162 | * and whatever implementor wants to do at this point. |
127 | * @param usi User-supplied identity | 163 | * @param usi User-supplied identity |
128 | */ | 164 | */ |
129 | virtual void initiate(const string& usi) = 0; | 165 | virtual void initiate(const string& usi) = 0; |
130 | /** | 166 | /** |
131 | * Prepare checkid_request. | 167 | * Prepare checkid_request. |
132 | * @param rv reference to the openid message to prepare | 168 | * @param rv reference to the openid message to prepare |
133 | * @param mode checkid_setup or checkid_immediate | 169 | * @param mode checkid_setup or checkid_immediate |
134 | * @param return_to the URL OP should redirect to after completion | 170 | * @param return_to the URL OP should redirect to after completion |
135 | * @param realm authentication realm to pass to OP | 171 | * @param realm authentication realm to pass to OP |
136 | * @param ext pointer to extension to use in request preparation | 172 | * @param ext pointer to extension to use in request preparation |
137 | * @return reference to the openid message | 173 | * @return reference to the openid message |
138 | */ | 174 | */ |
139 | basic_openid_message& checkid_( | 175 | basic_openid_message& checkid_( |
140 | basic_openid_message& rv, | 176 | basic_openid_message& rv, |
141 | mode_t mode, | 177 | mode_t mode, |
142 | const string& return_to,const string& realm, | 178 | const string& return_to,const string& realm, |
143 | extension_t *ext=0); | 179 | extension_t *ext=0); |
144 | /** | 180 | /** |
145 | * Verify assertion at the end of round-trip. | 181 | * Verify assertion at the end of round-trip. |
146 | * @param om incoming openid message | 182 | * @param om incoming openid message |
147 | * @param ext pointer to extention to use in parsing assertion | 183 | * @param ext pointer to extention to use in parsing assertion |
148 | * @throw id_res_setup if checkid_immediate request could not be | 184 | * @throw id_res_setup if checkid_immediate request could not be |
149 | * completed | 185 | * completed |
150 | * @throw id_res_cancel if authentication request was canceled | 186 | * @throw id_res_cancel if authentication request was canceled |
151 | * @throw id_res_mismatch in case of signature mismatch | 187 | * @throw id_res_mismatch in case of signature mismatch |
152 | * @throw id_res_bad_return_to if return_to url seems to be | 188 | * @throw id_res_bad_return_to if return_to url seems to be |
153 | * tampered with | 189 | * tampered with |
154 | * @throw id_res_unauthorized if OP is not authorized to make | 190 | * @throw id_res_unauthorized if OP is not authorized to make |
155 | * assertions regarding the identity | 191 | * assertions regarding the identity |
156 | */ | 192 | */ |
157 | void id_res(const basic_openid_message& om,extension_t *ext=0); | 193 | void id_res(const basic_openid_message& om,extension_t *ext=0); |
158 | 194 | ||
159 | /** | 195 | /** |
160 | * Establish association with OP | 196 | * Establish association with OP |
161 | * @param OP OP to establish association with | 197 | * @param OP OP to establish association with |
162 | * @throw dumb_RP if for a dumb RP | 198 | * @throw dumb_RP if for a dumb RP |
163 | */ | 199 | */ |
164 | virtual assoc_t associate(const string& OP); | 200 | virtual assoc_t associate(const string& OP); |
165 | /** | 201 | /** |
166 | * Check authentication with OP and invalidate handle if requested | 202 | * Check authentication with OP and invalidate handle if requested |
167 | * and confirmed | 203 | * and confirmed |
168 | * @param OP OP to check with | 204 | * @param OP OP to check with |
169 | * @param om message to check | 205 | * @param om message to check |
170 | * @throw failed_check_authentication if OP fails to confirm | 206 | * @throw failed_check_authentication if OP fails to confirm |
171 | * authenticity of the assertion | 207 | * authenticity of the assertion |
172 | */ | 208 | */ |
173 | void check_authentication(const string& OP,const basic_openid_message& om); | 209 | void check_authentication(const string& OP,const basic_openid_message& om); |
174 | /** | 210 | /** |
175 | * @} | 211 | * @} |
176 | */ | 212 | */ |
177 | 213 | ||
178 | /** | 214 | /** |
179 | * @name Miscellanea | 215 | * @name Miscellanea |
180 | * @{ | 216 | * @{ |
181 | */ | 217 | */ |
182 | /** | 218 | /** |
183 | * Verify OP authority. Return normally if OP is authorized to make | 219 | * Verify OP authority. Return normally if OP is authorized to make |
184 | * an assertion, throw an exception otherwise. | 220 | * an assertion, throw an exception otherwise. |
185 | * @param OP OP endpoint | 221 | * @param OP OP endpoint |
186 | * @param claimed_id claimed identity | 222 | * @param claimed_id claimed identity |
187 | * @param identity OP-Local identifier | 223 | * @param identity OP-Local identifier |
188 | * @throw id_res_unauthorized if OP is not authorized to make | 224 | * @throw id_res_unauthorized if OP is not authorized to make |
189 | * assertion regarding this identity. | 225 | * assertion regarding this identity. |
190 | */ | 226 | */ |
191 | virtual void verify_OP(const string& OP, | 227 | virtual void verify_OP(const string& OP, |
192 | const string& claimed_id,const string& identity) const = 0; | 228 | const string& claimed_id,const string& identity) const = 0; |
193 | /** | 229 | /** |
194 | * @} | 230 | * @} |
195 | */ | 231 | */ |
196 | }; | 232 | }; |
197 | 233 | ||
198 | } | 234 | } |
199 | 235 | ||
200 | #endif /* __OPKELE_BASIC_RP_H */ | 236 | #endif /* __OPKELE_BASIC_RP_H */ |
diff --git a/lib/basic_rp.cc b/lib/basic_rp.cc index e65d9fb..3357d0b 100644 --- a/lib/basic_rp.cc +++ b/lib/basic_rp.cc | |||
@@ -1,300 +1,325 @@ | |||
1 | #include <cassert> | 1 | #include <cassert> |
2 | #include <openssl/sha.h> | 2 | #include <openssl/sha.h> |
3 | #include <openssl/hmac.h> | 3 | #include <openssl/hmac.h> |
4 | #include <opkele/basic_rp.h> | 4 | #include <opkele/basic_rp.h> |
5 | #include <opkele/exception.h> | 5 | #include <opkele/exception.h> |
6 | #include <opkele/uris.h> | 6 | #include <opkele/uris.h> |
7 | #include <opkele/data.h> | 7 | #include <opkele/data.h> |
8 | #include <opkele/util.h> | 8 | #include <opkele/util.h> |
9 | #include <opkele/util-internal.h> | 9 | #include <opkele/util-internal.h> |
10 | #include <opkele/curl.h> | 10 | #include <opkele/curl.h> |
11 | #include <opkele/debug.h> | ||
11 | 12 | ||
12 | namespace opkele { | 13 | namespace opkele { |
13 | 14 | ||
15 | void basic_RP::reset_vars() { | ||
16 | claimed_id.clear(); identity.clear(); | ||
17 | } | ||
18 | |||
19 | const string& basic_RP::get_claimed_id() const { | ||
20 | if(claimed_id.empty()) | ||
21 | throw non_identity(OPKELE_CP_ "attempting to retreive claimed_id of non-identity assertion"); | ||
22 | assert(!identity.empty()); | ||
23 | return claimed_id; | ||
24 | } | ||
25 | |||
26 | const string& basic_RP::get_identity() const { | ||
27 | if(identity.empty()) | ||
28 | throw non_identity(OPKELE_CP_ "attempting to retrieve identity of non-identity related assertion"); | ||
29 | assert(!claimed_id.empty()); | ||
30 | return identity; | ||
31 | } | ||
32 | |||
14 | static void dh_get_secret( | 33 | static void dh_get_secret( |
15 | secret_t& secret, const basic_openid_message& om, | 34 | secret_t& secret, const basic_openid_message& om, |
16 | const char *exp_assoc, const char *exp_sess, | 35 | const char *exp_assoc, const char *exp_sess, |
17 | util::dh_t& dh, | 36 | util::dh_t& dh, |
18 | size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*), | 37 | size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*), |
19 | size_t exp_s_len) try { | 38 | size_t exp_s_len) try { |
20 | if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess) | 39 | if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess) |
21 | throw bad_input(OPKELE_CP_ "Unexpected associate response"); | 40 | throw bad_input(OPKELE_CP_ "Unexpected associate response"); |
22 | util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public")); | 41 | util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public")); |
23 | vector<unsigned char> ck(DH_size(dh)+1); | 42 | vector<unsigned char> ck(DH_size(dh)+1); |
24 | unsigned char *ckptr = &(ck.front())+1; | 43 | unsigned char *ckptr = &(ck.front())+1; |
25 | int cklen = DH_compute_key(ckptr,s_pub,dh); | 44 | int cklen = DH_compute_key(ckptr,s_pub,dh); |
26 | if(cklen<0) | 45 | if(cklen<0) |
27 | throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); | 46 | throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); |
28 | if(cklen && (*ckptr)&0x80) { | 47 | if(cklen && (*ckptr)&0x80) { |
29 | (*(--ckptr))=0; ++cklen; } | 48 | (*(--ckptr))=0; ++cklen; } |
30 | assert(d_len<=SHA256_DIGEST_LENGTH); | 49 | assert(d_len<=SHA256_DIGEST_LENGTH); |
31 | unsigned char key_digest[SHA256_DIGEST_LENGTH]; | 50 | unsigned char key_digest[SHA256_DIGEST_LENGTH]; |
32 | secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key")); | 51 | secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key")); |
33 | if(secret.size()!=exp_s_len) | 52 | if(secret.size()!=exp_s_len) |
34 | throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type"); | 53 | throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type"); |
35 | }catch(opkele::failed_lookup& ofl) { | 54 | }catch(opkele::failed_lookup& ofl) { |
36 | throw bad_input(OPKELE_CP_ "Incoherent response from OP"); | 55 | throw bad_input(OPKELE_CP_ "Incoherent response from OP"); |
37 | } OPKELE_RETHROW | 56 | } OPKELE_RETHROW |
38 | 57 | ||
39 | static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) { | 58 | static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) { |
40 | util::curl_pick_t curl = util::curl_pick_t::easy_init(); | 59 | util::curl_pick_t curl = util::curl_pick_t::easy_init(); |
41 | if(!curl) | 60 | if(!curl) |
42 | throw exception_curl(OPKELE_CP_ "failed to initialize curl"); | 61 | throw exception_curl(OPKELE_CP_ "failed to initialize curl"); |
43 | string request = inm.query_string(); | 62 | string request = inm.query_string(); |
44 | CURLcode r; | 63 | CURLcode r; |
45 | (r=curl.misc_sets()) | 64 | (r=curl.misc_sets()) |
46 | || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str())) | 65 | || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str())) |
47 | || (r=curl.easy_setopt(CURLOPT_POST,1)) | 66 | || (r=curl.easy_setopt(CURLOPT_POST,1)) |
48 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) | 67 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) |
49 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) | 68 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) |
50 | || (r=curl.set_write()); | 69 | || (r=curl.set_write()); |
51 | if(r) | 70 | if(r) |
52 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); | 71 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); |
53 | if( (r=curl.easy_perform()) ) | 72 | if( (r=curl.easy_perform()) ) |
54 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); | 73 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); |
55 | oum.from_keyvalues(curl.response); | 74 | oum.from_keyvalues(curl.response); |
56 | } | 75 | } |
57 | 76 | ||
58 | 77 | ||
59 | assoc_t basic_RP::associate(const string& OP) { | 78 | assoc_t basic_RP::associate(const string& OP) { |
60 | util::dh_t dh = DH_new(); | 79 | util::dh_t dh = DH_new(); |
61 | if(!dh) | 80 | if(!dh) |
62 | throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); | 81 | throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); |
63 | dh->p = util::dec_to_bignum(data::_default_p); | 82 | dh->p = util::dec_to_bignum(data::_default_p); |
64 | dh->g = util::dec_to_bignum(data::_default_g); | 83 | dh->g = util::dec_to_bignum(data::_default_g); |
65 | if(!DH_generate_key(dh)) | 84 | if(!DH_generate_key(dh)) |
66 | throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); | 85 | throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); |
67 | openid_message_t req; | 86 | openid_message_t req; |
68 | req.set_field("ns",OIURI_OPENID20); | 87 | req.set_field("ns",OIURI_OPENID20); |
69 | req.set_field("mode","associate"); | 88 | req.set_field("mode","associate"); |
70 | req.set_field("dh_modulus",util::bignum_to_base64(dh->p)); | 89 | req.set_field("dh_modulus",util::bignum_to_base64(dh->p)); |
71 | req.set_field("dh_gen",util::bignum_to_base64(dh->g)); | 90 | req.set_field("dh_gen",util::bignum_to_base64(dh->g)); |
72 | req.set_field("dh_consumer_public",util::bignum_to_base64(dh->pub_key)); | 91 | req.set_field("dh_consumer_public",util::bignum_to_base64(dh->pub_key)); |
73 | openid_message_t res; | 92 | openid_message_t res; |
74 | req.set_field("assoc_type","HMAC-SHA256"); | 93 | req.set_field("assoc_type","HMAC-SHA256"); |
75 | req.set_field("session_type","DH-SHA256"); | 94 | req.set_field("session_type","DH-SHA256"); |
76 | secret_t secret; | 95 | secret_t secret; |
77 | int expires_in; | 96 | int expires_in; |
78 | try { | 97 | try { |
79 | direct_request(res,req,OP); | 98 | direct_request(res,req,OP); |
80 | dh_get_secret( secret, res, | 99 | dh_get_secret( secret, res, |
81 | "HMAC-SHA256", "DH-SHA256", | 100 | "HMAC-SHA256", "DH-SHA256", |
82 | dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH ); | 101 | dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH ); |
83 | expires_in = util::string_to_long(res.get_field("expires_in")); | 102 | expires_in = util::string_to_long(res.get_field("expires_in")); |
84 | }catch(exception&) { | 103 | }catch(exception&) { |
85 | try { | 104 | try { |
86 | req.set_field("assoc_type","HMAC-SHA1"); | 105 | req.set_field("assoc_type","HMAC-SHA1"); |
87 | req.set_field("session_type","DH-SHA1"); | 106 | req.set_field("session_type","DH-SHA1"); |
88 | direct_request(res,req,OP); | 107 | direct_request(res,req,OP); |
89 | dh_get_secret( secret, res, | 108 | dh_get_secret( secret, res, |
90 | "HMAC-SHA1", "DH-SHA1", | 109 | "HMAC-SHA1", "DH-SHA1", |
91 | dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH ); | 110 | dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH ); |
92 | expires_in = util::string_to_long(res.get_field("expires_in")); | 111 | expires_in = util::string_to_long(res.get_field("expires_in")); |
93 | }catch(bad_input&) { | 112 | }catch(bad_input&) { |
94 | throw dumb_RP(OPKELE_CP_ "OP failed to supply an association"); | 113 | throw dumb_RP(OPKELE_CP_ "OP failed to supply an association"); |
95 | } | 114 | } |
96 | } | 115 | } |
97 | return store_assoc( | 116 | return store_assoc( |
98 | OP, res.get_field("assoc_handle"), | 117 | OP, res.get_field("assoc_handle"), |
99 | res.get_field("assoc_type"), secret, | 118 | res.get_field("assoc_type"), secret, |
100 | expires_in ); | 119 | expires_in ); |
101 | } | 120 | } |
102 | 121 | ||
103 | basic_openid_message& basic_RP::checkid_( | 122 | basic_openid_message& basic_RP::checkid_( |
104 | basic_openid_message& rv, | 123 | basic_openid_message& rv, |
105 | mode_t mode, | 124 | mode_t mode, |
106 | const string& return_to,const string& realm, | 125 | const string& return_to,const string& realm, |
107 | extension_t *ext) { | 126 | extension_t *ext) { |
108 | rv.reset_fields(); | 127 | rv.reset_fields(); |
109 | rv.set_field("ns",OIURI_OPENID20); | 128 | rv.set_field("ns",OIURI_OPENID20); |
110 | if(mode==mode_checkid_immediate) | 129 | if(mode==mode_checkid_immediate) |
111 | rv.set_field("mode","checkid_immediate"); | 130 | rv.set_field("mode","checkid_immediate"); |
112 | else if(mode==mode_checkid_setup) | 131 | else if(mode==mode_checkid_setup) |
113 | rv.set_field("mode","checkid_setup"); | 132 | rv.set_field("mode","checkid_setup"); |
114 | else | 133 | else |
115 | throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); | 134 | throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); |
116 | if(realm.empty() && return_to.empty()) | 135 | if(realm.empty() && return_to.empty()) |
117 | throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty"); | 136 | throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty"); |
118 | if(!realm.empty()) { | 137 | if(!realm.empty()) { |
119 | rv.set_field("realm",realm); | 138 | rv.set_field("realm",realm); |
120 | rv.set_field("trust_root",realm); | 139 | rv.set_field("trust_root",realm); |
121 | } | 140 | } |
122 | if(!return_to.empty()) | 141 | if(!return_to.empty()) |
123 | rv.set_field("return_to",return_to); | 142 | rv.set_field("return_to",return_to); |
124 | const openid_endpoint_t& ep = get_endpoint(); | 143 | const openid_endpoint_t& ep = get_endpoint(); |
125 | rv.set_field("claimed_id",ep.claimed_id); | 144 | rv.set_field("claimed_id",ep.claimed_id); |
126 | rv.set_field("identity",ep.local_id); | 145 | rv.set_field("identity",ep.local_id); |
127 | try { | 146 | try { |
128 | rv.set_field("assoc_handle",find_assoc(ep.uri)->handle()); | 147 | rv.set_field("assoc_handle",find_assoc(ep.uri)->handle()); |
129 | }catch(dumb_RP& drp) { | 148 | }catch(dumb_RP& drp) { |
130 | }catch(failed_lookup& fl) { | 149 | }catch(failed_lookup& fl) { |
131 | try { | 150 | try { |
132 | rv.set_field("assoc_handle",associate(ep.uri)->handle()); | 151 | rv.set_field("assoc_handle",associate(ep.uri)->handle()); |
133 | }catch(dumb_RP& drp) { } | 152 | }catch(dumb_RP& drp) { } |
134 | } OPKELE_RETHROW | 153 | } OPKELE_RETHROW |
135 | if(ext) ext->rp_checkid_hook(rv); | 154 | if(ext) ext->rp_checkid_hook(rv); |
136 | return rv; | 155 | return rv; |
137 | } | 156 | } |
138 | 157 | ||
139 | class signed_part_message_proxy : public basic_openid_message { | 158 | class signed_part_message_proxy : public basic_openid_message { |
140 | public: | 159 | public: |
141 | const basic_openid_message& x; | 160 | const basic_openid_message& x; |
142 | set<string> signeds; | 161 | set<string> signeds; |
143 | 162 | ||
144 | signed_part_message_proxy(const basic_openid_message& xx) : x(xx) { | 163 | signed_part_message_proxy(const basic_openid_message& xx) : x(xx) { |
145 | const string& slist = x.get_field("signed"); | 164 | const string& slist = x.get_field("signed"); |
146 | string::size_type p = 0; | 165 | string::size_type p = 0; |
147 | while(true) { | 166 | while(true) { |
148 | string::size_type co = slist.find(',',p); | 167 | string::size_type co = slist.find(',',p); |
149 | string f = (co==string::npos) | 168 | string f = (co==string::npos) |
150 | ?slist.substr(p):slist.substr(p,co-p); | 169 | ?slist.substr(p):slist.substr(p,co-p); |
151 | signeds.insert(f); | 170 | signeds.insert(f); |
152 | if(co==string::npos) break; | 171 | if(co==string::npos) break; |
153 | p = co+1; | 172 | p = co+1; |
154 | } | 173 | } |
155 | } | 174 | } |
156 | 175 | ||
157 | bool has_field(const string& n) const { | 176 | bool has_field(const string& n) const { |
158 | return signeds.find(n)!=signeds.end() && x.has_field(n); } | 177 | return signeds.find(n)!=signeds.end() && x.has_field(n); } |
159 | const string& get_field(const string& n) const { | 178 | const string& get_field(const string& n) const { |
160 | if(signeds.find(n)==signeds.end()) | 179 | if(signeds.find(n)==signeds.end()) |
161 | throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed"); | 180 | throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed"); |
162 | return x.get_field(n); } | 181 | return x.get_field(n); } |
163 | 182 | ||
164 | fields_iterator fields_begin() const { | 183 | fields_iterator fields_begin() const { |
165 | return signeds.begin(); } | 184 | return signeds.begin(); } |
166 | fields_iterator fields_end() const { | 185 | fields_iterator fields_end() const { |
167 | return signeds.end(); } | 186 | return signeds.end(); } |
168 | }; | 187 | }; |
169 | 188 | ||
170 | static void parse_query(const string& u,string::size_type q, | 189 | static void parse_query(const string& u,string::size_type q, |
171 | map<string,string>& p) { | 190 | map<string,string>& p) { |
172 | if(q==string::npos) | 191 | if(q==string::npos) |
173 | return; | 192 | return; |
174 | assert(u[q]=='?'); | 193 | assert(u[q]=='?'); |
175 | ++q; | 194 | ++q; |
176 | string::size_type l = u.size(); | 195 | string::size_type l = u.size(); |
177 | while(q<l) { | 196 | while(q<l) { |
178 | string::size_type eq = u.find('=',q); | 197 | string::size_type eq = u.find('=',q); |
179 | string::size_type am = u.find('&',q); | 198 | string::size_type am = u.find('&',q); |
180 | if(am==string::npos) { | 199 | if(am==string::npos) { |
181 | if(eq==string::npos) { | 200 | if(eq==string::npos) { |
182 | p[""] = u.substr(q); | 201 | p[""] = u.substr(q); |
183 | }else{ | 202 | }else{ |
184 | p[u.substr(q,eq-q)] = u.substr(eq+1); | 203 | p[u.substr(q,eq-q)] = u.substr(eq+1); |
185 | } | 204 | } |
186 | break; | 205 | break; |
187 | }else{ | 206 | }else{ |
188 | if(eq==string::npos || eq>am) { | 207 | if(eq==string::npos || eq>am) { |
189 | p[""] = u.substr(q,eq-q); | 208 | p[""] = u.substr(q,eq-q); |
190 | }else{ | 209 | }else{ |
191 | p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1); | 210 | p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1); |
192 | } | 211 | } |
193 | q = ++am; | 212 | q = ++am; |
194 | } | 213 | } |
195 | } | 214 | } |
196 | } | 215 | } |
197 | 216 | ||
198 | void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) { | 217 | void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) { |
218 | reset_vars(); | ||
199 | bool o2 = om.has_field("ns") | 219 | bool o2 = om.has_field("ns") |
200 | && om.get_field("ns")==OIURI_OPENID20; | 220 | && om.get_field("ns")==OIURI_OPENID20; |
201 | if( (!o2) && om.has_field("user_setup_url")) | 221 | if( (!o2) && om.has_field("user_setup_url")) |
202 | throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided", | 222 | throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided", |
203 | om.get_field("user_setup_url")); | 223 | om.get_field("user_setup_url")); |
204 | string m = om.get_field("mode"); | 224 | string m = om.get_field("mode"); |
205 | if(o2 && m=="setup_needed") | 225 | if(o2 && m=="setup_needed") |
206 | throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided"); | 226 | throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided"); |
207 | if(m=="cancel") | 227 | if(m=="cancel") |
208 | throw id_res_cancel(OPKELE_CP_ "authentication cancelled"); | 228 | throw id_res_cancel(OPKELE_CP_ "authentication cancelled"); |
209 | bool go_dumb=false; | 229 | bool go_dumb=false; |
210 | try { | 230 | try { |
211 | string OP = o2 | 231 | string OP = o2 |
212 | ?om.get_field("op_endpoint") | 232 | ?om.get_field("op_endpoint") |
213 | :get_endpoint().uri; | 233 | :get_endpoint().uri; |
214 | assoc_t assoc = retrieve_assoc( | 234 | assoc_t assoc = retrieve_assoc( |
215 | OP,om.get_field("assoc_handle")); | 235 | OP,om.get_field("assoc_handle")); |
216 | if(om.get_field("sig")!=util::base64_signature(assoc,om)) | 236 | if(om.get_field("sig")!=util::base64_signature(assoc,om)) |
217 | throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); | 237 | throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); |
218 | }catch(dumb_RP& drp) { | 238 | }catch(dumb_RP& drp) { |
219 | go_dumb=true; | 239 | go_dumb=true; |
220 | }catch(failed_lookup& e) { | 240 | }catch(failed_lookup& e) { |
221 | go_dumb=true; | 241 | go_dumb=true; |
222 | } OPKELE_RETHROW | 242 | } OPKELE_RETHROW |
223 | if(go_dumb) { | 243 | if(go_dumb) { |
224 | try { | 244 | try { |
225 | string OP = o2 | 245 | string OP = o2 |
226 | ?om.get_field("op_endpoint") | 246 | ?om.get_field("op_endpoint") |
227 | :get_endpoint().uri; | 247 | :get_endpoint().uri; |
228 | check_authentication(OP,om); | 248 | check_authentication(OP,om); |
229 | }catch(failed_check_authentication& fca) { | 249 | }catch(failed_check_authentication& fca) { |
230 | throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); | 250 | throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); |
231 | } OPKELE_RETHROW | 251 | } OPKELE_RETHROW |
232 | } | 252 | } |
233 | signed_part_message_proxy signeds(om); | 253 | signed_part_message_proxy signeds(om); |
234 | if(o2) { | 254 | if(o2) { |
235 | check_nonce(om.get_field("op_endpoint"), | 255 | check_nonce(om.get_field("op_endpoint"), |
236 | om.get_field("response_nonce")); | 256 | om.get_field("response_nonce")); |
237 | static const char *mustsign[] = { | 257 | static const char *mustsign[] = { |
238 | "op_endpoint", "return_to", "response_nonce", "assoc_handle", | 258 | "op_endpoint", "return_to", "response_nonce", "assoc_handle", |
239 | "claimed_id", "identity" }; | 259 | "claimed_id", "identity" }; |
240 | for(size_t ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) { | 260 | for(size_t ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) { |
241 | if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms])) | 261 | if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms])) |
242 | throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs"); | 262 | throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs"); |
243 | } | 263 | } |
244 | if( ( | 264 | if( ( |
245 | (om.has_field("claimed_id")?1:0) | 265 | (om.has_field("claimed_id")?1:0) |
246 | ^ | 266 | ^ |
247 | (om.has_field("identity")?1:0) | 267 | (om.has_field("identity")?1:0) |
248 | )&1 ) | 268 | )&1 ) |
249 | throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent"); | 269 | throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent"); |
250 | 270 | ||
251 | string turl = util::rfc_3986_normalize_uri(get_this_url()); | 271 | string turl = util::rfc_3986_normalize_uri(get_this_url()); |
252 | util::strip_uri_fragment_part(turl); | 272 | util::strip_uri_fragment_part(turl); |
253 | string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to")); | 273 | string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to")); |
254 | util::strip_uri_fragment_part(rurl); | 274 | util::strip_uri_fragment_part(rurl); |
255 | string::size_type | 275 | string::size_type |
256 | tq = turl.find('?'), rq = rurl.find('?'); | 276 | tq = turl.find('?'), rq = rurl.find('?'); |
257 | if( | 277 | if( |
258 | ((tq==string::npos)?turl:turl.substr(0,tq)) | 278 | ((tq==string::npos)?turl:turl.substr(0,tq)) |
259 | != | 279 | != |
260 | ((rq==string::npos)?rurl:rurl.substr(0,rq)) | 280 | ((rq==string::npos)?rurl:rurl.substr(0,rq)) |
261 | ) | 281 | ) |
262 | throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url"); | 282 | throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url"); |
263 | map<string,string> tp; parse_query(turl,tq,tp); | 283 | map<string,string> tp; parse_query(turl,tq,tp); |
264 | map<string,string> rp; parse_query(rurl,rq,rp); | 284 | map<string,string> rp; parse_query(rurl,rq,rp); |
265 | for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) { | 285 | for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) { |
266 | map<string,string>::const_iterator tpi = tp.find(rpi->first); | 286 | map<string,string>::const_iterator tpi = tp.find(rpi->first); |
267 | if(tpi==tp.end()) | 287 | if(tpi==tp.end()) |
268 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request"); | 288 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request"); |
269 | if(tpi->second!=rpi->second) | 289 | if(tpi->second!=rpi->second) |
270 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request"); | 290 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request"); |
271 | } | 291 | } |
272 | 292 | ||
273 | if(om.has_field("claimed_id")) { | 293 | if(om.has_field("claimed_id")) { |
294 | claimed_id = om.get_field("claimed_id"); | ||
295 | identity = om.get_field("identity"); | ||
274 | verify_OP( | 296 | verify_OP( |
275 | om.get_field("op_endpoint"), | 297 | om.get_field("op_endpoint"), |
276 | om.get_field("claimed_id"), | 298 | claimed_id, identity ); |
277 | om.get_field("identity") ); | ||
278 | } | 299 | } |
279 | 300 | ||
301 | }else{ | ||
302 | claimed_id = get_endpoint().claimed_id; | ||
303 | /* TODO: check if this is the identity we asked for */ | ||
304 | identity = om.get_field("identity"); | ||
280 | } | 305 | } |
281 | if(ext) ext->rp_id_res_hook(om,signeds); | 306 | if(ext) ext->rp_id_res_hook(om,signeds); |
282 | } | 307 | } |
283 | 308 | ||
284 | void basic_RP::check_authentication(const string& OP, | 309 | void basic_RP::check_authentication(const string& OP, |
285 | const basic_openid_message& om){ | 310 | const basic_openid_message& om){ |
286 | openid_message_t res; | 311 | openid_message_t res; |
287 | static const string checkauthmode = "check_authentication"; | 312 | static const string checkauthmode = "check_authentication"; |
288 | direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP); | 313 | direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP); |
289 | if(res.has_field("is_valid")) { | 314 | if(res.has_field("is_valid")) { |
290 | if(res.get_field("is_valid")=="true") { | 315 | if(res.get_field("is_valid")=="true") { |
291 | if(res.has_field("invalidate_handle")) | 316 | if(res.has_field("invalidate_handle")) |
292 | invalidate_assoc(OP,res.get_field("invalidate_handle")); | 317 | invalidate_assoc(OP,res.get_field("invalidate_handle")); |
293 | return; | 318 | return; |
294 | } | 319 | } |
295 | } | 320 | } |
296 | throw failed_check_authentication( | 321 | throw failed_check_authentication( |
297 | OPKELE_CP_ "failed to verify response"); | 322 | OPKELE_CP_ "failed to verify response"); |
298 | } | 323 | } |
299 | 324 | ||
300 | } | 325 | } |