summaryrefslogtreecommitdiffabout
authorMichael Krelin <hacker@klever.net>2007-08-09 11:31:28 (UTC)
committer Michael Krelin <hacker@klever.net>2007-08-09 11:31:28 (UTC)
commitccabea66fe29ae41458d47d7004d28e225caf41a (patch) (unidiff)
treeeb7e6cc2afa3fffe7eafad439113ba2363a74278
parent65bab7c9f984d6fe45ce72e7db014c40eba4d240 (diff)
downloadlibopkele-ccabea66fe29ae41458d47d7004d28e225caf41a.zip
libopkele-ccabea66fe29ae41458d47d7004d28e225caf41a.tar.gz
libopkele-ccabea66fe29ae41458d47d7004d28e225caf41a.tar.bz2
fix to previous commit
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--include/opkele/types.h5
-rw-r--r--lib/consumer.cc2
2 files changed, 5 insertions, 2 deletions
diff --git a/include/opkele/types.h b/include/opkele/types.h
index 757c0af..8f4bf73 100644
--- a/include/opkele/types.h
+++ b/include/opkele/types.h
@@ -1,165 +1,170 @@
1#ifndef __OPKELE_TYPES_H 1#ifndef __OPKELE_TYPES_H
2#define __OPKELE_TYPES_H 2#define __OPKELE_TYPES_H
3 3
4/** 4/**
5 * @file 5 * @file
6 * @brief various types declarations 6 * @brief various types declarations
7 */ 7 */
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 <memory> 13#include <memory>
14 14
15namespace opkele { 15namespace opkele {
16 using std::vector; 16 using std::vector;
17 using std::string; 17 using std::string;
18 using std::map; 18 using std::map;
19 using std::ostream; 19 using std::ostream;
20 using std::auto_ptr; 20 using std::auto_ptr;
21 21
22 /** 22 /**
23 * the OpenID operation mode 23 * the OpenID operation mode
24 */ 24 */
25 typedef enum _mode_t { 25 typedef enum _mode_t {
26 mode_associate, 26 mode_associate,
27 mode_checkid_immediate, 27 mode_checkid_immediate,
28 mode_checkid_setup, 28 mode_checkid_setup,
29 mode_check_association 29 mode_check_association
30 } mode_t; 30 } mode_t;
31 31
32 /** 32 /**
33 * the association secret container 33 * the association secret container
34 */ 34 */
35 class secret_t : public vector<unsigned char> { 35 class secret_t : public vector<unsigned char> {
36 public: 36 public:
37 37
38 /** 38 /**
39 * xor the secret and hmac together and encode, using base64 39 * xor the secret and hmac together and encode, using base64
40 * @param key_sha1 pointer to the sha1 digest 40 * @param key_sha1 pointer to the sha1 digest
41 * @param rv reference to the return value 41 * @param rv reference to the return value
42 */ 42 */
43 void enxor_to_base64(const unsigned char *key_sha1,string& rv) const; 43 void enxor_to_base64(const unsigned char *key_sha1,string& rv) const;
44 /** 44 /**
45 * decode base64-encoded secret and xor it with the sha1 digest 45 * decode base64-encoded secret and xor it with the sha1 digest
46 * @param key_sha1 pointer to the message digest 46 * @param key_sha1 pointer to the message digest
47 * @param b64 base64-encoded secret value 47 * @param b64 base64-encoded secret value
48 */ 48 */
49 void enxor_from_base64(const unsigned char *key_sha1,const string& b64); 49 void enxor_from_base64(const unsigned char *key_sha1,const string& b64);
50 /** 50 /**
51 * plainly encode to base64 representation 51 * plainly encode to base64 representation
52 * @param rv reference to the return value 52 * @param rv reference to the return value
53 */ 53 */
54 void to_base64(string& rv) const; 54 void to_base64(string& rv) const;
55 /** 55 /**
56 * decode cleartext secret from base64 56 * decode cleartext secret from base64
57 * @param b64 base64-encoded representation of the secret value 57 * @param b64 base64-encoded representation of the secret value
58 */ 58 */
59 void from_base64(const string& b64); 59 void from_base64(const string& b64);
60 }; 60 };
61 61
62 /** 62 /**
63 * Interface to the association. 63 * Interface to the association.
64 */ 64 */
65 class association_t { 65 class association_t {
66 public: 66 public:
67 67
68 /** 68 /**
69 * retrieve the server with which association was established. 69 * retrieve the server with which association was established.
70 * @return server name 70 * @return server name
71 */ 71 */
72 virtual string server() const = 0; 72 virtual string server() const = 0;
73 /** 73 /**
74 * retrieve the association handle. 74 * retrieve the association handle.
75 * @return handle 75 * @return handle
76 */ 76 */
77 virtual string handle() const = 0; 77 virtual string handle() const = 0;
78 /** 78 /**
79 * retrieve the association type. 79 * retrieve the association type.
80 * @return association type 80 * @return association type
81 */ 81 */
82 virtual string assoc_type() const = 0; 82 virtual string assoc_type() const = 0;
83 /** 83 /**
84 * retrieve the association secret. 84 * retrieve the association secret.
85 * @return association secret 85 * @return association secret
86 */ 86 */
87 virtual secret_t secret() const = 0; 87 virtual secret_t secret() const = 0;
88 /** 88 /**
89 * retrieve the number of seconds the association expires in. 89 * retrieve the number of seconds the association expires in.
90 * @return seconds till expiration 90 * @return seconds till expiration
91 */ 91 */
92 virtual int expires_in() const = 0; 92 virtual int expires_in() const = 0;
93 /** 93 /**
94 * check whether the association is stateless. 94 * check whether the association is stateless.
95 * @return true if stateless 95 * @return true if stateless
96 */ 96 */
97 virtual bool stateless() const = 0; 97 virtual bool stateless() const = 0;
98 /**
99 * check whether the association is expired.
100 * @return true if expired
101 */
102 virtual bool is_expired() const = 0;
98 }; 103 };
99 104
100 /** 105 /**
101 * the auto_ptr<> for association_t object type 106 * the auto_ptr<> for association_t object type
102 */ 107 */
103 typedef auto_ptr<association_t> assoc_t; 108 typedef auto_ptr<association_t> assoc_t;
104 109
105 /** 110 /**
106 * request/response parameters map 111 * request/response parameters map
107 */ 112 */
108 class params_t : public map<string,string> { 113 class params_t : public map<string,string> {
109 public: 114 public:
110 115
111 /** 116 /**
112 * check whether the parameter is present. 117 * check whether the parameter is present.
113 * @param n the parameter name 118 * @param n the parameter name
114 * @return true if yes 119 * @return true if yes
115 */ 120 */
116 bool has_param(const string& n) const; 121 bool has_param(const string& n) const;
117 /** 122 /**
118 * retrieve the parameter (const version) 123 * retrieve the parameter (const version)
119 * @param n the parameter name 124 * @param n the parameter name
120 * @return the parameter value 125 * @return the parameter value
121 * @throw failed_lookup if there is no such parameter 126 * @throw failed_lookup if there is no such parameter
122 */ 127 */
123 const string& get_param(const string& n) const; 128 const string& get_param(const string& n) const;
124 /** 129 /**
125 * retrieve the parameter. 130 * retrieve the parameter.
126 * @param n the parameter name 131 * @param n the parameter name
127 * @return the parameter value 132 * @return the parameter value
128 * @throw failed_lookup if there is no such parameter 133 * @throw failed_lookup if there is no such parameter
129 */ 134 */
130 string& get_param(const string& n); 135 string& get_param(const string& n);
131 136
132 /** 137 /**
133 * parse the OpenID key/value data. 138 * parse the OpenID key/value data.
134 * @param kv the OpenID key/value data 139 * @param kv the OpenID key/value data
135 */ 140 */
136 void parse_keyvalues(const string& kv); 141 void parse_keyvalues(const string& kv);
137 /** 142 /**
138 * sign the fields. 143 * sign the fields.
139 * @param secret the secret used for signing 144 * @param secret the secret used for signing
140 * @param sig reference to the string, containing base64-encoded 145 * @param sig reference to the string, containing base64-encoded
141 * result 146 * result
142 * @param slist the comma-separated list of fields to sign 147 * @param slist the comma-separated list of fields to sign
143 * @param prefix the string to prepend to parameter names 148 * @param prefix the string to prepend to parameter names
144 */ 149 */
145 void sign(secret_t secret,string& sig,const string& slist,const char *prefix=0) const; 150 void sign(secret_t secret,string& sig,const string& slist,const char *prefix=0) const;
146 151
147 /** 152 /**
148 * append parameters to the URL as a GET-request parameters. 153 * append parameters to the URL as a GET-request parameters.
149 * @param url the base URL 154 * @param url the base URL
150 * @param prefix the string to prepend to parameter names 155 * @param prefix the string to prepend to parameter names
151 * @return the ready-to-use location 156 * @return the ready-to-use location
152 */ 157 */
153 string append_query(const string& url,const char *prefix = "openid.") const; 158 string append_query(const string& url,const char *prefix = "openid.") const;
154 }; 159 };
155 160
156 /** 161 /**
157 * dump the key/value pairs for the parameters to the stream. 162 * dump the key/value pairs for the parameters to the stream.
158 * @param o output stream 163 * @param o output stream
159 * @param p the parameters 164 * @param p the parameters
160 */ 165 */
161 ostream& operator << (ostream& o,const params_t& p); 166 ostream& operator << (ostream& o,const params_t& p);
162 167
163} 168}
164 169
165#endif /* __OPKELE_TYPES_H */ 170#endif /* __OPKELE_TYPES_H */
diff --git a/lib/consumer.cc b/lib/consumer.cc
index af309c1..8f66688 100644
--- a/lib/consumer.cc
+++ b/lib/consumer.cc
@@ -1,373 +1,371 @@
1#include <algorithm> 1#include <algorithm>
2#include <cassert> 2#include <cassert>
3#include <opkele/util.h> 3#include <opkele/util.h>
4#include <opkele/exception.h> 4#include <opkele/exception.h>
5#include <opkele/data.h> 5#include <opkele/data.h>
6#include <opkele/consumer.h> 6#include <opkele/consumer.h>
7#include <openssl/sha.h> 7#include <openssl/sha.h>
8#include <openssl/hmac.h> 8#include <openssl/hmac.h>
9#include <curl/curl.h> 9#include <curl/curl.h>
10#include <pcre++.h> 10#include <pcre++.h>
11 11
12#include <iostream> 12#include <iostream>
13 13
14#include "config.h" 14#include "config.h"
15 15
16namespace opkele { 16namespace opkele {
17 using namespace std; 17 using namespace std;
18 18
19 class curl_t { 19 class curl_t {
20 public: 20 public:
21 CURL *_c; 21 CURL *_c;
22 22
23 curl_t() : _c(0) { } 23 curl_t() : _c(0) { }
24 curl_t(CURL *c) : _c(c) { } 24 curl_t(CURL *c) : _c(c) { }
25 ~curl_t() throw() { if(_c) curl_easy_cleanup(_c); } 25 ~curl_t() throw() { if(_c) curl_easy_cleanup(_c); }
26 26
27 curl_t& operator=(CURL *c) { if(_c) curl_easy_cleanup(_c); _c=c; return *this; } 27 curl_t& operator=(CURL *c) { if(_c) curl_easy_cleanup(_c); _c=c; return *this; }
28 28
29 operator const CURL*(void) const { return _c; } 29 operator const CURL*(void) const { return _c; }
30 operator CURL*(void) { return _c; } 30 operator CURL*(void) { return _c; }
31 }; 31 };
32 32
33 static CURLcode curl_misc_sets(CURL* c) { 33 static CURLcode curl_misc_sets(CURL* c) {
34 CURLcode r; 34 CURLcode r;
35 (r=curl_easy_setopt(c,CURLOPT_FOLLOWLOCATION,1)) 35 (r=curl_easy_setopt(c,CURLOPT_FOLLOWLOCATION,1))
36 || (r=curl_easy_setopt(c,CURLOPT_MAXREDIRS,5)) 36 || (r=curl_easy_setopt(c,CURLOPT_MAXREDIRS,5))
37 || (r=curl_easy_setopt(c,CURLOPT_DNS_CACHE_TIMEOUT,120)) 37 || (r=curl_easy_setopt(c,CURLOPT_DNS_CACHE_TIMEOUT,120))
38 || (r=curl_easy_setopt(c,CURLOPT_DNS_USE_GLOBAL_CACHE,1)) 38 || (r=curl_easy_setopt(c,CURLOPT_DNS_USE_GLOBAL_CACHE,1))
39 || (r=curl_easy_setopt(c,CURLOPT_USERAGENT,PACKAGE_NAME"/"PACKAGE_VERSION)) 39 || (r=curl_easy_setopt(c,CURLOPT_USERAGENT,PACKAGE_NAME"/"PACKAGE_VERSION))
40 || (r=curl_easy_setopt(c,CURLOPT_TIMEOUT,20)) 40 || (r=curl_easy_setopt(c,CURLOPT_TIMEOUT,20))
41 #ifdefDISABLE_CURL_SSL_VERIFYHOST 41 #ifdefDISABLE_CURL_SSL_VERIFYHOST
42 || (r=curl_easy_setopt(c,CURLOPT_SSL_VERIFYHOST,0)) 42 || (r=curl_easy_setopt(c,CURLOPT_SSL_VERIFYHOST,0))
43#endif 43#endif
44 #ifdefDISABLE_CURL_SSL_VERIFYPEER 44 #ifdefDISABLE_CURL_SSL_VERIFYPEER
45 || (r=curl_easy_setopt(c,CURLOPT_SSL_VERIFYPEER,0)) 45 || (r=curl_easy_setopt(c,CURLOPT_SSL_VERIFYPEER,0))
46#endif 46#endif
47 ; 47 ;
48 return r; 48 return r;
49 } 49 }
50 50
51 static size_t _curl_tostring(void *ptr,size_t size,size_t nmemb,void *stream) { 51 static size_t _curl_tostring(void *ptr,size_t size,size_t nmemb,void *stream) {
52 string *str = (string*)stream; 52 string *str = (string*)stream;
53 size_t bytes = size*nmemb; 53 size_t bytes = size*nmemb;
54 size_t get = min(16384-str->length(),bytes); 54 size_t get = min(16384-str->length(),bytes);
55 str->append((const char*)ptr,get); 55 str->append((const char*)ptr,get);
56 return get; 56 return get;
57 } 57 }
58 58
59 assoc_t consumer_t::associate(const string& server) { 59 assoc_t consumer_t::associate(const string& server) {
60 util::dh_t dh = DH_new(); 60 util::dh_t dh = DH_new();
61 if(!dh) 61 if(!dh)
62 throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); 62 throw exception_openssl(OPKELE_CP_ "failed to DH_new()");
63 dh->p = util::dec_to_bignum(data::_default_p); 63 dh->p = util::dec_to_bignum(data::_default_p);
64 dh->g = util::dec_to_bignum(data::_default_g); 64 dh->g = util::dec_to_bignum(data::_default_g);
65 if(!DH_generate_key(dh)) 65 if(!DH_generate_key(dh))
66 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); 66 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()");
67 string request = 67 string request =
68 "openid.mode=associate" 68 "openid.mode=associate"
69 "&openid.assoc_type=HMAC-SHA1" 69 "&openid.assoc_type=HMAC-SHA1"
70 "&openid.session_type=DH-SHA1" 70 "&openid.session_type=DH-SHA1"
71 "&openid.dh_consumer_public="; 71 "&openid.dh_consumer_public=";
72 request += util::url_encode(util::bignum_to_base64(dh->pub_key)); 72 request += util::url_encode(util::bignum_to_base64(dh->pub_key));
73 curl_t curl = curl_easy_init(); 73 curl_t curl = curl_easy_init();
74 if(!curl) 74 if(!curl)
75 throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()"); 75 throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()");
76 string response; 76 string response;
77 CURLcode r; 77 CURLcode r;
78 (r=curl_misc_sets(curl)) 78 (r=curl_misc_sets(curl))
79 || (r=curl_easy_setopt(curl,CURLOPT_URL,server.c_str())) 79 || (r=curl_easy_setopt(curl,CURLOPT_URL,server.c_str()))
80 || (r=curl_easy_setopt(curl,CURLOPT_POST,1)) 80 || (r=curl_easy_setopt(curl,CURLOPT_POST,1))
81 || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDS,request.data())) 81 || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDS,request.data()))
82 || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDSIZE,request.length())) 82 || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDSIZE,request.length()))
83 || (r=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curl_tostring)) 83 || (r=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curl_tostring))
84 || (r=curl_easy_setopt(curl,CURLOPT_WRITEDATA,&response)) 84 || (r=curl_easy_setopt(curl,CURLOPT_WRITEDATA,&response))
85 ; 85 ;
86 if(r) 86 if(r)
87 throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r); 87 throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r);
88 if(r=curl_easy_perform(curl)) 88 if(r=curl_easy_perform(curl))
89 throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r); 89 throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r);
90 params_t p; p.parse_keyvalues(response); 90 params_t p; p.parse_keyvalues(response);
91 if(p.has_param("assoc_type") && p.get_param("assoc_type")!="HMAC-SHA1") 91 if(p.has_param("assoc_type") && p.get_param("assoc_type")!="HMAC-SHA1")
92 throw bad_input(OPKELE_CP_ "unsupported assoc_type"); 92 throw bad_input(OPKELE_CP_ "unsupported assoc_type");
93 string st; 93 string st;
94 if(p.has_param("session_type")) st = p.get_param("session_type"); 94 if(p.has_param("session_type")) st = p.get_param("session_type");
95 if((!st.empty()) && st!="DH-SHA1") 95 if((!st.empty()) && st!="DH-SHA1")
96 throw bad_input(OPKELE_CP_ "unsupported session_type"); 96 throw bad_input(OPKELE_CP_ "unsupported session_type");
97 secret_t secret; 97 secret_t secret;
98 if(st.empty()) { 98 if(st.empty()) {
99 secret.from_base64(p.get_param("mac_key")); 99 secret.from_base64(p.get_param("mac_key"));
100 }else{ 100 }else{
101 util::bignum_t s_pub = util::base64_to_bignum(p.get_param("dh_server_public")); 101 util::bignum_t s_pub = util::base64_to_bignum(p.get_param("dh_server_public"));
102 vector<unsigned char> ck(DH_size(dh)+1); 102 vector<unsigned char> ck(DH_size(dh)+1);
103 unsigned char *ckptr = &(ck.front())+1; 103 unsigned char *ckptr = &(ck.front())+1;
104 int cklen = DH_compute_key(ckptr,s_pub,dh); 104 int cklen = DH_compute_key(ckptr,s_pub,dh);
105 if(cklen<0) 105 if(cklen<0)
106 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); 106 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()");
107 if(cklen && (*ckptr)&0x80) { 107 if(cklen && (*ckptr)&0x80) {
108 (*(--ckptr)) = 0; ++cklen; 108 (*(--ckptr)) = 0; ++cklen;
109 } 109 }
110 unsigned char key_sha1[SHA_DIGEST_LENGTH]; 110 unsigned char key_sha1[SHA_DIGEST_LENGTH];
111 SHA1(ckptr,cklen,key_sha1); 111 SHA1(ckptr,cklen,key_sha1);
112 secret.enxor_from_base64(key_sha1,p.get_param("enc_mac_key")); 112 secret.enxor_from_base64(key_sha1,p.get_param("enc_mac_key"));
113 } 113 }
114 int expires_in = 0; 114 int expires_in = 0;
115 if(p.has_param("expires_in")) { 115 if(p.has_param("expires_in")) {
116 expires_in = util::string_to_long(p.get_param("expires_in")); 116 expires_in = util::string_to_long(p.get_param("expires_in"));
117 }else if(p.has_param("issued") && p.has_param("expiry")) { 117 }else if(p.has_param("issued") && p.has_param("expiry")) {
118 expires_in = util::w3c_to_time(p.get_param("expiry"))-util::w3c_to_time(p.get_param("issued")); 118 expires_in = util::w3c_to_time(p.get_param("expiry"))-util::w3c_to_time(p.get_param("issued"));
119 }else 119 }else
120 throw bad_input(OPKELE_CP_ "no expiration information"); 120 throw bad_input(OPKELE_CP_ "no expiration information");
121 return store_assoc(server,p.get_param("assoc_handle"),secret,expires_in); 121 return store_assoc(server,p.get_param("assoc_handle"),secret,expires_in);
122 } 122 }
123 123
124 string consumer_t::checkid_immediate(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { 124 string consumer_t::checkid_immediate(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) {
125 return checkid_(mode_checkid_immediate,identity,return_to,trust_root,ext); 125 return checkid_(mode_checkid_immediate,identity,return_to,trust_root,ext);
126 } 126 }
127 string consumer_t::checkid_setup(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { 127 string consumer_t::checkid_setup(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) {
128 return checkid_(mode_checkid_setup,identity,return_to,trust_root,ext); 128 return checkid_(mode_checkid_setup,identity,return_to,trust_root,ext);
129 } 129 }
130 string consumer_t::checkid_(mode_t mode,const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { 130 string consumer_t::checkid_(mode_t mode,const string& identity,const string& return_to,const string& trust_root,extension_t *ext) {
131 params_t p; 131 params_t p;
132 if(mode==mode_checkid_immediate) 132 if(mode==mode_checkid_immediate)
133 p["mode"]="checkid_immediate"; 133 p["mode"]="checkid_immediate";
134 else if(mode==mode_checkid_setup) 134 else if(mode==mode_checkid_setup)
135 p["mode"]="checkid_setup"; 135 p["mode"]="checkid_setup";
136 else 136 else
137 throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); 137 throw bad_input(OPKELE_CP_ "unknown checkid_* mode");
138 string iurl = canonicalize(identity); 138 string iurl = canonicalize(identity);
139 string server, delegate; 139 string server, delegate;
140 retrieve_links(iurl,server,delegate); 140 retrieve_links(iurl,server,delegate);
141 p["identity"] = delegate.empty()?iurl:delegate; 141 p["identity"] = delegate.empty()?iurl:delegate;
142 if(!trust_root.empty()) 142 if(!trust_root.empty())
143 p["trust_root"] = trust_root; 143 p["trust_root"] = trust_root;
144 p["return_to"] = return_to; 144 p["return_to"] = return_to;
145 try { 145 try {
146 string ah = find_assoc(server)->handle(); 146 string ah = find_assoc(server)->handle();
147 if(ah->is_expired()) /* TODO: or should I throw some other exception to force programmer fix his implementation? */
148 throw failed_lookup(OPKELE_CP_ "find_assoc() has returned expired handle");
149 p["assoc_handle"] = ah; 147 p["assoc_handle"] = ah;
150 }catch(failed_lookup& fl) { 148 }catch(failed_lookup& fl) {
151 string ah = associate(server)->handle(); 149 string ah = associate(server)->handle();
152 p["assoc_handle"] = ah; 150 p["assoc_handle"] = ah;
153 } 151 }
154 if(ext) ext->checkid_hook(p,identity); 152 if(ext) ext->checkid_hook(p,identity);
155 return p.append_query(server); 153 return p.append_query(server);
156 } 154 }
157 155
158 void consumer_t::id_res(const params_t& pin,const string& identity,extension_t *ext) { 156 void consumer_t::id_res(const params_t& pin,const string& identity,extension_t *ext) {
159 if(pin.has_param("openid.user_setup_url")) 157 if(pin.has_param("openid.user_setup_url"))
160 throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",pin.get_param("openid.user_setup_url")); 158 throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",pin.get_param("openid.user_setup_url"));
161 string server,delegate; 159 string server,delegate;
162 retrieve_links(identity.empty()?pin.get_param("openid.identity"):canonicalize(identity),server,delegate); 160 retrieve_links(identity.empty()?pin.get_param("openid.identity"):canonicalize(identity),server,delegate);
163 params_t ps; 161 params_t ps;
164 try { 162 try {
165 assoc_t assoc = retrieve_assoc(server,pin.get_param("openid.assoc_handle")); 163 assoc_t assoc = retrieve_assoc(server,pin.get_param("openid.assoc_handle"));
166 if(assoc->is_expired()) /* TODO: or should I throw some other exception to force programmer fix his implementation? */ 164 if(assoc->is_expired()) /* TODO: or should I throw some other exception to force programmer fix his implementation? */
167 throw failed_lookup(OPKELE_CP_ "retrieve_assoc() has returned expired handle"); 165 throw failed_lookup(OPKELE_CP_ "retrieve_assoc() has returned expired handle");
168 const string& sigenc = pin.get_param("openid.sig"); 166 const string& sigenc = pin.get_param("openid.sig");
169 vector<unsigned char> sig; 167 vector<unsigned char> sig;
170 util::decode_base64(sigenc,sig); 168 util::decode_base64(sigenc,sig);
171 const string& slist = pin.get_param("openid.signed"); 169 const string& slist = pin.get_param("openid.signed");
172 string kv; 170 string kv;
173 string::size_type p = 0; 171 string::size_type p = 0;
174 while(true) { 172 while(true) {
175 string::size_type co = slist.find(',',p); 173 string::size_type co = slist.find(',',p);
176 string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p); 174 string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p);
177 kv += f; 175 kv += f;
178 kv += ':'; 176 kv += ':';
179 f.insert(0,"openid."); 177 f.insert(0,"openid.");
180 kv += pin.get_param(f); 178 kv += pin.get_param(f);
181 kv += '\n'; 179 kv += '\n';
182 if(ext) ps[f.substr(sizeof("openid.")-1)] = pin.get_param(f); 180 if(ext) ps[f.substr(sizeof("openid.")-1)] = pin.get_param(f);
183 if(co==string::npos) 181 if(co==string::npos)
184 break; 182 break;
185 p = co+1; 183 p = co+1;
186 } 184 }
187 secret_t secret = assoc->secret(); 185 secret_t secret = assoc->secret();
188 unsigned int md_len = 0; 186 unsigned int md_len = 0;
189 unsigned char *md = HMAC( 187 unsigned char *md = HMAC(
190 EVP_sha1(), 188 EVP_sha1(),
191 &(secret.front()),secret.size(), 189 &(secret.front()),secret.size(),
192 (const unsigned char *)kv.data(),kv.length(), 190 (const unsigned char *)kv.data(),kv.length(),
193 0,&md_len); 191 0,&md_len);
194 if(sig.size()!=md_len || memcmp(&(sig.front()),md,md_len)) 192 if(sig.size()!=md_len || memcmp(&(sig.front()),md,md_len))
195 throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); 193 throw id_res_mismatch(OPKELE_CP_ "signature mismatch");
196 }catch(failed_lookup& e) { /* XXX: more specific? */ 194 }catch(failed_lookup& e) { /* XXX: more specific? */
197 const string& slist = pin.get_param("openid.signed"); 195 const string& slist = pin.get_param("openid.signed");
198 string::size_type pp = 0; 196 string::size_type pp = 0;
199 params_t p; 197 params_t p;
200 while(true) { 198 while(true) {
201 string::size_type co = slist.find(',',pp); 199 string::size_type co = slist.find(',',pp);
202 string f = "openid."; 200 string f = "openid.";
203 f += (co==string::npos)?slist.substr(pp):slist.substr(pp,co-pp); 201 f += (co==string::npos)?slist.substr(pp):slist.substr(pp,co-pp);
204 p[f] = pin.get_param(f); 202 p[f] = pin.get_param(f);
205 if(co==string::npos) 203 if(co==string::npos)
206 break; 204 break;
207 pp = co+1; 205 pp = co+1;
208 } 206 }
209 p["openid.assoc_handle"] = pin.get_param("openid.assoc_handle"); 207 p["openid.assoc_handle"] = pin.get_param("openid.assoc_handle");
210 p["openid.sig"] = pin.get_param("openid.sig"); 208 p["openid.sig"] = pin.get_param("openid.sig");
211 p["openid.signed"] = pin.get_param("openid.signed"); 209 p["openid.signed"] = pin.get_param("openid.signed");
212 try { 210 try {
213 string ih = pin.get_param("openid.invalidate_handle"); 211 string ih = pin.get_param("openid.invalidate_handle");
214 p["openid.invalidate_handle"] = ih; 212 p["openid.invalidate_handle"] = ih;
215 }catch(failed_lookup& fl) { } 213 }catch(failed_lookup& fl) { }
216 try { 214 try {
217 check_authentication(server,p); 215 check_authentication(server,p);
218 }catch(failed_check_authentication& fca) { 216 }catch(failed_check_authentication& fca) {
219 throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); 217 throw id_res_failed(OPKELE_CP_ "failed to check_authentication()");
220 } 218 }
221 } 219 }
222 if(ext) ext->id_res_hook(pin,ps,identity); 220 if(ext) ext->id_res_hook(pin,ps,identity);
223 } 221 }
224 222
225 void consumer_t::check_authentication(const string& server,const params_t& p) { 223 void consumer_t::check_authentication(const string& server,const params_t& p) {
226 string request = "openid.mode=check_authentication"; 224 string request = "openid.mode=check_authentication";
227 for(params_t::const_iterator i=p.begin();i!=p.end();++i) { 225 for(params_t::const_iterator i=p.begin();i!=p.end();++i) {
228 if(i->first!="openid.mode") { 226 if(i->first!="openid.mode") {
229 request += '&'; 227 request += '&';
230 request += i->first; 228 request += i->first;
231 request += '='; 229 request += '=';
232 request += util::url_encode(i->second); 230 request += util::url_encode(i->second);
233 } 231 }
234 } 232 }
235 curl_t curl = curl_easy_init(); 233 curl_t curl = curl_easy_init();
236 if(!curl) 234 if(!curl)
237 throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()"); 235 throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()");
238 string response; 236 string response;
239 CURLcode r; 237 CURLcode r;
240 (r=curl_misc_sets(curl)) 238 (r=curl_misc_sets(curl))
241 || (r=curl_easy_setopt(curl,CURLOPT_URL,server.c_str())) 239 || (r=curl_easy_setopt(curl,CURLOPT_URL,server.c_str()))
242 || (r=curl_easy_setopt(curl,CURLOPT_POST,1)) 240 || (r=curl_easy_setopt(curl,CURLOPT_POST,1))
243 || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDS,request.data())) 241 || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDS,request.data()))
244 || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDSIZE,request.length())) 242 || (r=curl_easy_setopt(curl,CURLOPT_POSTFIELDSIZE,request.length()))
245 || (r=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curl_tostring)) 243 || (r=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curl_tostring))
246 || (r=curl_easy_setopt(curl,CURLOPT_WRITEDATA,&response)) 244 || (r=curl_easy_setopt(curl,CURLOPT_WRITEDATA,&response))
247 ; 245 ;
248 if(r) 246 if(r)
249 throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r); 247 throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r);
250 if(r=curl_easy_perform(curl)) 248 if(r=curl_easy_perform(curl))
251 throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r); 249 throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r);
252 params_t pp; pp.parse_keyvalues(response); 250 params_t pp; pp.parse_keyvalues(response);
253 if(pp.has_param("invalidate_handle")) 251 if(pp.has_param("invalidate_handle"))
254 invalidate_assoc(server,pp.get_param("invalidate_handle")); 252 invalidate_assoc(server,pp.get_param("invalidate_handle"));
255 if(pp.has_param("is_valid")) { 253 if(pp.has_param("is_valid")) {
256 if(pp.get_param("is_valid")=="true") 254 if(pp.get_param("is_valid")=="true")
257 return; 255 return;
258 }else if(pp.has_param("lifetime")) { 256 }else if(pp.has_param("lifetime")) {
259 if(util::string_to_long(pp.get_param("lifetime"))) 257 if(util::string_to_long(pp.get_param("lifetime")))
260 return; 258 return;
261 } 259 }
262 throw failed_check_authentication(OPKELE_CP_ "failed to verify response"); 260 throw failed_check_authentication(OPKELE_CP_ "failed to verify response");
263 } 261 }
264 262
265 void consumer_t::retrieve_links(const string& url,string& server,string& delegate) { 263 void consumer_t::retrieve_links(const string& url,string& server,string& delegate) {
266 server.erase(); 264 server.erase();
267 delegate.erase(); 265 delegate.erase();
268 curl_t curl = curl_easy_init(); 266 curl_t curl = curl_easy_init();
269 if(!curl) 267 if(!curl)
270 throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()"); 268 throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()");
271 string html; 269 string html;
272 CURLcode r; 270 CURLcode r;
273 (r=curl_misc_sets(curl)) 271 (r=curl_misc_sets(curl))
274 || (r=curl_easy_setopt(curl,CURLOPT_URL,url.c_str())) 272 || (r=curl_easy_setopt(curl,CURLOPT_URL,url.c_str()))
275 || (r=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curl_tostring)) 273 || (r=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curl_tostring))
276 || (r=curl_easy_setopt(curl,CURLOPT_WRITEDATA,&html)) 274 || (r=curl_easy_setopt(curl,CURLOPT_WRITEDATA,&html))
277 ; 275 ;
278 if(r) 276 if(r)
279 throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r); 277 throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r);
280 r = curl_easy_perform(curl); 278 r = curl_easy_perform(curl);
281 if(r && r!=CURLE_WRITE_ERROR) 279 if(r && r!=CURLE_WRITE_ERROR)
282 throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r); 280 throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r);
283 pcrepp::Pcre bre("<body\\b",PCRE_CASELESS); 281 pcrepp::Pcre bre("<body\\b",PCRE_CASELESS);
284 // strip out everything past body 282 // strip out everything past body
285 if(bre.search(html)) 283 if(bre.search(html))
286 html.erase(bre.get_match_start()); 284 html.erase(bre.get_match_start());
287 pcrepp::Pcre hdre("<head[^>]*>",PCRE_CASELESS); 285 pcrepp::Pcre hdre("<head[^>]*>",PCRE_CASELESS);
288 if(!hdre.search(html)) 286 if(!hdre.search(html))
289 throw bad_input(OPKELE_CP_ "failed to find head"); 287 throw bad_input(OPKELE_CP_ "failed to find head");
290 html.erase(0,hdre.get_match_end()+1); 288 html.erase(0,hdre.get_match_end()+1);
291 pcrepp::Pcre lre("<link\\b([^>]+)>",PCRE_CASELESS), 289 pcrepp::Pcre lre("<link\\b([^>]+)>",PCRE_CASELESS),
292 rre("\\brel=['\"]([^'\"]+)['\"]",PCRE_CASELESS), 290 rre("\\brel=['\"]([^'\"]+)['\"]",PCRE_CASELESS),
293 hre("\\bhref=['\"]([^'\"]+)['\"]",PCRE_CASELESS); 291 hre("\\bhref=['\"]([^'\"]+)['\"]",PCRE_CASELESS);
294 while(lre.search(html)) { 292 while(lre.search(html)) {
295 string attrs = lre[0]; 293 string attrs = lre[0];
296 html.erase(0,lre.get_match_end()+1); 294 html.erase(0,lre.get_match_end()+1);
297 if(!(rre.search(attrs)&&hre.search(attrs))) 295 if(!(rre.search(attrs)&&hre.search(attrs)))
298 continue; 296 continue;
299 if(rre[0]=="openid.server") { 297 if(rre[0]=="openid.server") {
300 server = hre[0]; 298 server = hre[0];
301 if(!delegate.empty()) 299 if(!delegate.empty())
302 break; 300 break;
303 }else if(rre[0]=="openid.delegate") { 301 }else if(rre[0]=="openid.delegate") {
304 delegate = hre[0]; 302 delegate = hre[0];
305 if(!server.empty()) 303 if(!server.empty())
306 break; 304 break;
307 } 305 }
308 } 306 }
309 if(server.empty()) 307 if(server.empty())
310 throw failed_assertion(OPKELE_CP_ "The location has no openid.server declaration"); 308 throw failed_assertion(OPKELE_CP_ "The location has no openid.server declaration");
311 } 309 }
312 310
313 assoc_t consumer_t::find_assoc(const string& server) { 311 assoc_t consumer_t::find_assoc(const string& server) {
314 throw failed_lookup(OPKELE_CP_ "no find_assoc() provided"); 312 throw failed_lookup(OPKELE_CP_ "no find_assoc() provided");
315 } 313 }
316 314
317 string consumer_t::normalize(const string& url) { 315 string consumer_t::normalize(const string& url) {
318 string rv = url; 316 string rv = url;
319 // strip leading and trailing spaces 317 // strip leading and trailing spaces
320 string::size_type i = rv.find_first_not_of(" \t\r\n"); 318 string::size_type i = rv.find_first_not_of(" \t\r\n");
321 if(i==string::npos) 319 if(i==string::npos)
322 throw bad_input(OPKELE_CP_ "empty URL"); 320 throw bad_input(OPKELE_CP_ "empty URL");
323 if(i) 321 if(i)
324 rv.erase(0,i); 322 rv.erase(0,i);
325 i = rv.find_last_not_of(" \t\r\n"); 323 i = rv.find_last_not_of(" \t\r\n");
326 assert(i!=string::npos); 324 assert(i!=string::npos);
327 if(i<(rv.length()-1)) 325 if(i<(rv.length()-1))
328 rv.erase(i+1); 326 rv.erase(i+1);
329 // add missing http:// 327 // add missing http://
330 i = rv.find("://"); 328 i = rv.find("://");
331 if(i==string::npos) { // primitive. but do we need more? 329 if(i==string::npos) { // primitive. but do we need more?
332 rv.insert(0,"http://"); 330 rv.insert(0,"http://");
333 i = sizeof("http://")-1; 331 i = sizeof("http://")-1;
334 }else{ 332 }else{
335 i += sizeof("://")-1; 333 i += sizeof("://")-1;
336 } 334 }
337 string::size_type qm = rv.find('?',i); 335 string::size_type qm = rv.find('?',i);
338 string::size_type sl = rv.find('/',i); 336 string::size_type sl = rv.find('/',i);
339 if(qm!=string::npos) { 337 if(qm!=string::npos) {
340 if(sl==string::npos || sl>qm) 338 if(sl==string::npos || sl>qm)
341 rv.insert(qm,1,'/'); 339 rv.insert(qm,1,'/');
342 }else{ 340 }else{
343 if(sl==string::npos) 341 if(sl==string::npos)
344 rv += '/'; 342 rv += '/';
345 } 343 }
346 return rv; 344 return rv;
347 } 345 }
348 346
349 string consumer_t::canonicalize(const string& url) { 347 string consumer_t::canonicalize(const string& url) {
350 string rv = normalize(url); 348 string rv = normalize(url);
351 curl_t curl = curl_easy_init(); 349 curl_t curl = curl_easy_init();
352 if(!curl) 350 if(!curl)
353 throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()"); 351 throw exception_curl(OPKELE_CP_ "failed to curl_easy_init()");
354 string html; 352 string html;
355 CURLcode r; 353 CURLcode r;
356 (r=curl_misc_sets(curl)) 354 (r=curl_misc_sets(curl))
357 || (r=curl_easy_setopt(curl,CURLOPT_URL,rv.c_str())) 355 || (r=curl_easy_setopt(curl,CURLOPT_URL,rv.c_str()))
358 || (r=curl_easy_setopt(curl,CURLOPT_NOBODY,1)) 356 || (r=curl_easy_setopt(curl,CURLOPT_NOBODY,1))
359 ; 357 ;
360 if(r) 358 if(r)
361 throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r); 359 throw exception_curl(OPKELE_CP_ "failed to curl_easy_setopt()",r);
362 r = curl_easy_perform(curl); 360 r = curl_easy_perform(curl);
363 if(r) 361 if(r)
364 throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r); 362 throw exception_curl(OPKELE_CP_ "failed to curl_easy_perform()",r);
365 const char *eu = 0; 363 const char *eu = 0;
366 r = curl_easy_getinfo(curl,CURLINFO_EFFECTIVE_URL,&eu); 364 r = curl_easy_getinfo(curl,CURLINFO_EFFECTIVE_URL,&eu);
367 if(r) 365 if(r)
368 throw exception_curl(OPKELE_CP_ "failed to curl_easy_getinfo(..CURLINFO_EFFECTIVE_URL..)",r); 366 throw exception_curl(OPKELE_CP_ "failed to curl_easy_getinfo(..CURLINFO_EFFECTIVE_URL..)",r);
369 rv = eu; 367 rv = eu;
370 return normalize(rv); 368 return normalize(rv);
371 } 369 }
372 370
373} 371}