summaryrefslogtreecommitdiffabout
authorMichael Krelin <hacker@klever.net>2008-02-19 10:52:09 (UTC)
committer Michael Krelin <hacker@klever.net>2008-02-19 10:52:09 (UTC)
commit42e4fb613d190508b3e8b8993d233044eeea4d20 (patch) (unidiff)
tree9b8ebc420942554f927a777e03c70a7c65305a88
parenta3db32747e8370cab8cfdcc382fee875613b7b77 (diff)
downloadlibopkele-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>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--include/opkele/basic_rp.h36
-rw-r--r--lib/basic_rp.cc29
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
8namespace opkele { 8namespace 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
12namespace opkele { 13namespace 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}