summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--include/Makefile.am3
-rw-r--r--include/opkele/util-internal.h92
-rw-r--r--include/opkele/util.h92
-rw-r--r--lib/basic_op.cc1
-rw-r--r--lib/basic_rp.cc1
-rw-r--r--lib/consumer.cc1
-rw-r--r--lib/server.cc1
-rw-r--r--test/idiscover.cc1
8 files changed, 110 insertions, 82 deletions
diff --git a/include/Makefile.am b/include/Makefile.am
index 9f5982c..f842bb9 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,32 +1,33 @@
1NODIST_HEADERS_ = \ 1NODIST_HEADERS_ = \
2 opkele/acconfig.h \ 2 opkele/acconfig.h \
3 opkele/tr1-mem.h 3 opkele/tr1-mem.h
4 4
5nobase_include_HEADERS = \ 5nobase_include_HEADERS = \
6 opkele/opkele-config.h \ 6 opkele/opkele-config.h \
7 opkele/types.h \ 7 opkele/types.h \
8 opkele/association.h \ 8 opkele/association.h \
9 opkele/exception.h \ 9 opkele/exception.h \
10 opkele/server.h \ 10 opkele/server.h \
11 opkele/consumer.h \ 11 opkele/consumer.h \
12 opkele/extension.h \ 12 opkele/extension.h \
13 opkele/sreg.h \ 13 opkele/sreg.h \
14 opkele/extension_chain.h \ 14 opkele/extension_chain.h \
15 opkele/xconsumer.h \ 15 opkele/xconsumer.h \
16 opkele/xserver.h \ 16 opkele/xserver.h \
17 opkele/uris.h \ 17 opkele/uris.h \
18 opkele/tr1-mem.h \ 18 opkele/tr1-mem.h \
19 opkele/basic_rp.h opkele/prequeue_rp.h \ 19 opkele/basic_rp.h opkele/prequeue_rp.h \
20 opkele/iterator.h \ 20 opkele/iterator.h \
21 opkele/basic_op.h opkele/verify_op.h \ 21 opkele/basic_op.h opkele/verify_op.h \
22 opkele/util.h \
22 ${NODIST_HEADERS_} 23 ${NODIST_HEADERS_}
23 24
24noinst_HEADERS = \ 25noinst_HEADERS = \
25 opkele/data.h \ 26 opkele/data.h \
26 opkele/curl.h opkele/expat.h opkele/tidy.h \ 27 opkele/curl.h opkele/expat.h opkele/tidy.h \
27 opkele/util.h \ 28 opkele/util-internal.h \
28 opkele/debug.h \ 29 opkele/debug.h \
29 opkele/discovery.h 30 opkele/discovery.h
30 31
31dist-hook: 32dist-hook:
32 rm -f $(addprefix ${distdir}/,${NODIST_HEADERS_}) 33 rm -f $(addprefix ${distdir}/,${NODIST_HEADERS_})
diff --git a/include/opkele/util-internal.h b/include/opkele/util-internal.h
new file mode 100644
index 0000000..ec091ce
--- a/dev/null
+++ b/include/opkele/util-internal.h
@@ -0,0 +1,92 @@
1#ifndef __OPKELE_UTIL_INTERNAL_H
2#define __OPKELE_UTIL_INTERNAL_H
3
4#include <openssl/bn.h>
5#include <openssl/dh.h>
6
7namespace opkele {
8 namespace util {
9
10 /**
11 * Convenience class encapsulating SSL BIGNUM object for the purpose of
12 * automatical freeing.
13 */
14 class bignum_t {
15 public:
16 BIGNUM *_bn;
17
18 bignum_t() : _bn(0) { }
19 bignum_t(BIGNUM *bn) : _bn(bn) { }
20 ~bignum_t() throw() { if(_bn) BN_free(_bn); }
21
22 bignum_t& operator=(BIGNUM *bn) { if(_bn) BN_free(_bn); _bn = bn; return *this; }
23
24 operator const BIGNUM*(void) const { return _bn; }
25 operator BIGNUM*(void) { return _bn; }
26 };
27 /**
28 * Convenience clas encapsulating SSL DH object for the purpose of
29 * automatic freeing.
30 */
31 class dh_t {
32 public:
33 DH *_dh;
34
35 dh_t() : _dh(0) { }
36 dh_t(DH *dh) : _dh(dh) { }
37 ~dh_t() throw() { if(_dh) DH_free(_dh); }
38
39 dh_t& operator=(DH *dh) { if(_dh) DH_free(_dh); _dh = dh; return *this; }
40
41 operator const DH*(void) const { return _dh; }
42 operator DH*(void) { return _dh; }
43
44 DH* operator->() { return _dh; }
45 const DH* operator->() const { return _dh; }
46 };
47
48 /**
49 * Convert base64-encoded SSL BIGNUM to internal representation.
50 * @param b64 base64-encoded number
51 * @return SSL BIGNUM
52 * @throw failed_conversion in case of error
53 */
54 BIGNUM *base64_to_bignum(const string& b64);
55 /**
56 * Convert decimal representation to SSL BIGNUM.
57 * @param dec decimal representation
58 * @return resulting BIGNUM
59 * @throw failed_conversion in case of error
60 */
61 BIGNUM *dec_to_bignum(const string& dec);
62 /**
63 * Convert SSL BIGNUM data to base64 encoded string.
64 * @param bn BIGNUM
65 * @return base64encoded string
66 */
67 string bignum_to_base64(const BIGNUM *bn);
68
69 string abi_demangle(const char* mn);
70
71 class change_mode_message_proxy : public basic_openid_message {
72 public:
73 const basic_openid_message& x;
74 const string& mode;
75
76 change_mode_message_proxy(const basic_openid_message& xx,const string& m) : x(xx), mode(m) { }
77
78 bool has_field(const string& n) const { return x.has_field(n); }
79 const string& get_field(const string& n) const {
80 return (n=="mode")?mode:x.get_field(n); }
81 bool has_ns(const string& uri) const {return x.has_ns(uri); }
82 string get_ns(const string& uri) const { return x.get_ns(uri); }
83 fields_iterator fields_begin() const {
84 return x.fields_begin(); }
85 fields_iterator fields_end() const {
86 return x.fields_end(); }
87 };
88
89 }
90}
91
92#endif /* __OPKELE_UTIL_INTERNAL_H */
diff --git a/include/opkele/util.h b/include/opkele/util.h
index bc1a0ea..60955e1 100644
--- a/include/opkele/util.h
+++ b/include/opkele/util.h
@@ -1,184 +1,114 @@
1#ifndef __OPKELE_UTIL_H 1#ifndef __OPKELE_UTIL_H
2#define __OPKELE_UTIL_H 2#define __OPKELE_UTIL_H
3 3
4#include <time.h> 4#include <time.h>
5#include <string> 5#include <string>
6#include <vector> 6#include <vector>
7#include <openssl/bn.h>
8#include <openssl/dh.h>
9#include <opkele/types.h> 7#include <opkele/types.h>
10 8
11namespace opkele { 9namespace opkele {
12 using std::string; 10 using std::string;
13 using std::vector; 11 using std::vector;
14 12
15 /** 13 /**
16 * @brief opkele utils namespace 14 * @brief opkele utils namespace
17 */ 15 */
18 namespace util { 16 namespace util {
19 17
20 /** 18 /**
21 * Convenience class encapsulating SSL BIGNUM object for the purpose of
22 * automatical freeing.
23 */
24 class bignum_t {
25 public:
26 BIGNUM *_bn;
27
28 bignum_t() : _bn(0) { }
29 bignum_t(BIGNUM *bn) : _bn(bn) { }
30 ~bignum_t() throw() { if(_bn) BN_free(_bn); }
31
32 bignum_t& operator=(BIGNUM *bn) { if(_bn) BN_free(_bn); _bn = bn; return *this; }
33
34 operator const BIGNUM*(void) const { return _bn; }
35 operator BIGNUM*(void) { return _bn; }
36 };
37 /**
38 * Convenience clas encapsulating SSL DH object for the purpose of
39 * automatic freeing.
40 */
41 class dh_t {
42 public:
43 DH *_dh;
44
45 dh_t() : _dh(0) { }
46 dh_t(DH *dh) : _dh(dh) { }
47 ~dh_t() throw() { if(_dh) DH_free(_dh); }
48
49 dh_t& operator=(DH *dh) { if(_dh) DH_free(_dh); _dh = dh; return *this; }
50
51 operator const DH*(void) const { return _dh; }
52 operator DH*(void) { return _dh; }
53
54 DH* operator->() { return _dh; }
55 const DH* operator->() const { return _dh; }
56 };
57
58 /**
59 * Convert base64-encoded SSL BIGNUM to internal representation.
60 * @param b64 base64-encoded number
61 * @return SSL BIGNUM
62 * @throw failed_conversion in case of error
63 */
64 BIGNUM *base64_to_bignum(const string& b64);
65 /**
66 * Convert decimal representation to SSL BIGNUM.
67 * @param dec decimal representation
68 * @return resulting BIGNUM
69 * @throw failed_conversion in case of error
70 */
71 BIGNUM *dec_to_bignum(const string& dec);
72 /**
73 * Convert SSL BIGNUM data to base64 encoded string.
74 * @param bn BIGNUM
75 * @return base64encoded string
76 */
77 string bignum_to_base64(const BIGNUM *bn);
78
79 /**
80 * Convert internal time representation to w3c format 19 * Convert internal time representation to w3c format
81 * @param t internal representation 20 * @param t internal representation
82 * @return w3c time 21 * @return w3c time
83 * @throw failed_conversion in case of error 22 * @throw failed_conversion in case of error
84 */ 23 */
85 string time_to_w3c(time_t t); 24 string time_to_w3c(time_t t);
86 /** 25 /**
87 * Convert W3C time representation to internal time_t 26 * Convert W3C time representation to internal time_t
88 * @param w w3c representation 27 * @param w w3c representation
89 * @return converted time 28 * @return converted time
90 * @throw failed_conversion in case of error 29 * @throw failed_conversion in case of error
91 */ 30 */
92 time_t w3c_to_time(const string& w); 31 time_t w3c_to_time(const string& w);
93 32
94 /** 33 /**
95 * Encode string to the representation suitable for using in URL. 34 * Encode string to the representation suitable for using in URL.
96 * @param str string to encode 35 * @param str string to encode
97 * @return encoded string 36 * @return encoded string
98 * @throw failed_conversion in case of failure 37 * @throw failed_conversion in case of failure
99 */ 38 */
100 string url_encode(const string& str); 39 string url_encode(const string& str);
101 40
102 /** 41 /**
103 * Make string suitable for using as x(ht)ml attribute. 42 * Make string suitable for using as x(ht)ml attribute.
104 * @param str string to escape 43 * @param str string to escape
105 * @return escaped string 44 * @return escaped string
106 */ 45 */
107 string attr_escape(const string& str); 46 string attr_escape(const string& str);
108 47
109 /** 48 /**
110 * Convert number to string 49 * Convert number to string
111 * @param l number 50 * @param l number
112 * @return string representation 51 * @return string representation
113 * @throw failed_conversion in case of failure 52 * @throw failed_conversion in case of failure
114 */ 53 */
115 string long_to_string(long l); 54 string long_to_string(long l);
116 /** 55 /**
117 * Convert string to number 56 * Convert string to number
118 * @param s string, containing the number 57 * @param s string, containing the number
119 * @return the number 58 * @return the number
120 * @throw failed_conversion in case of failure 59 * @throw failed_conversion in case of failure
121 */ 60 */
122 long string_to_long(const string& s); 61 long string_to_long(const string& s);
123 62
124 /** 63 /**
125 * Encode binary data using base64. 64 * Encode binary data using base64.
126 * @param data pointer to binary data 65 * @param data pointer to binary data
127 * @param length length of data 66 * @param length length of data
128 * @return encoded data 67 * @return encoded data
129 */ 68 */
130 string encode_base64(const void *data,size_t length); 69 string encode_base64(const void *data,size_t length);
131 /** 70 /**
132 * Decode binary data from base64 representation. 71 * Decode binary data from base64 representation.
133 * @param data base64-encoded data 72 * @param data base64-encoded data
134 * @param rv container for decoded binary 73 * @param rv container for decoded binary
135 */ 74 */
136 void decode_base64(const string& data,vector<unsigned char>& rv); 75 void decode_base64(const string& data,vector<unsigned char>& rv);
137 76
138 /** 77 /**
139 * Normalize http(s) URI according to RFC3986, section 6. URI is 78 * Normalize http(s) URI according to RFC3986, section 6. URI is
140 * expected to have scheme: in front of it. 79 * expected to have scheme: in front of it.
141 * @param uri URI 80 * @param uri URI
142 * @return normalized URI 81 * @return normalized URI
143 * @throw not_implemented in case of non-httpi(s) URI 82 * @throw not_implemented in case of non-httpi(s) URI
144 * @throw bad_input in case of malformed URI 83 * @throw bad_input in case of malformed URI
145 */ 84 */
146 string rfc_3986_normalize_uri(const string& uri); 85 string rfc_3986_normalize_uri(const string& uri);
147 86
148 /** 87 /**
149 * Match URI against realm 88 * Match URI against realm
150 * @param uri URI to match 89 * @param uri URI to match
151 * @param realm realm to match against 90 * @param realm realm to match against
152 * @return true if URI matches realm 91 * @return true if URI matches realm
153 */ 92 */
154 bool uri_matches_realm(const string& uri,const string& realm); 93 bool uri_matches_realm(const string& uri,const string& realm);
155 94
95 /**
96 * Strip fragment part from URI
97 * @param uri input/output parameter containing the URI
98 * @return reference to uri
99 */
156 string& strip_uri_fragment_part(string& uri); 100 string& strip_uri_fragment_part(string& uri);
157 101
158 string abi_demangle(const char* mn); 102 /**
159 103 * Calculate signature and encode it using base64
104 * @param assoc association being used for signing
105 * @param om openid message
106 * @return base64 representation of the signature
107 */
160 string base64_signature(const assoc_t& assoc,const basic_openid_message& om); 108 string base64_signature(const assoc_t& assoc,const basic_openid_message& om);
161 109
162 class change_mode_message_proxy : public basic_openid_message {
163 public:
164 const basic_openid_message& x;
165 const string& mode;
166
167 change_mode_message_proxy(const basic_openid_message& xx,const string& m) : x(xx), mode(m) { }
168
169 bool has_field(const string& n) const { return x.has_field(n); }
170 const string& get_field(const string& n) const {
171 return (n=="mode")?mode:x.get_field(n); }
172 bool has_ns(const string& uri) const {return x.has_ns(uri); }
173 string get_ns(const string& uri) const { return x.get_ns(uri); }
174 fields_iterator fields_begin() const {
175 return x.fields_begin(); }
176 fields_iterator fields_end() const {
177 return x.fields_end(); }
178 };
179
180 } 110 }
181 111
182} 112}
183 113
184#endif /* __OPKELE_UTIL_H */ 114#endif /* __OPKELE_UTIL_H */
diff --git a/lib/basic_op.cc b/lib/basic_op.cc
index c247493..fa659ac 100644
--- a/lib/basic_op.cc
+++ b/lib/basic_op.cc
@@ -1,327 +1,328 @@
1#include <time.h> 1#include <time.h>
2#include <cassert> 2#include <cassert>
3#include <openssl/sha.h> 3#include <openssl/sha.h>
4#include <openssl/hmac.h> 4#include <openssl/hmac.h>
5#include <opkele/data.h> 5#include <opkele/data.h>
6#include <opkele/basic_op.h> 6#include <opkele/basic_op.h>
7#include <opkele/exception.h> 7#include <opkele/exception.h>
8#include <opkele/util.h> 8#include <opkele/util.h>
9#include <opkele/util-internal.h>
9#include <opkele/uris.h> 10#include <opkele/uris.h>
10 11
11namespace opkele { 12namespace opkele {
12 13
13 void basic_OP::reset_vars() { 14 void basic_OP::reset_vars() {
14 assoc.reset(); 15 assoc.reset();
15 return_to.clear(); realm.clear(); 16 return_to.clear(); realm.clear();
16 claimed_id.clear(); identity.clear(); 17 claimed_id.clear(); identity.clear();
17 invalidate_handle.clear(); 18 invalidate_handle.clear();
18 } 19 }
19 20
20 bool basic_OP::has_return_to() const { 21 bool basic_OP::has_return_to() const {
21 return !return_to.empty(); 22 return !return_to.empty();
22 } 23 }
23 const string& basic_OP::get_return_to() const { 24 const string& basic_OP::get_return_to() const {
24 if(return_to.empty()) 25 if(return_to.empty())
25 throw no_return_to(OPKELE_CP_ "No return_to URL provided with request"); 26 throw no_return_to(OPKELE_CP_ "No return_to URL provided with request");
26 return return_to; 27 return return_to;
27 } 28 }
28 29
29 const string& basic_OP::get_realm() const { 30 const string& basic_OP::get_realm() const {
30 assert(!realm.empty()); 31 assert(!realm.empty());
31 return realm; 32 return realm;
32 } 33 }
33 34
34 bool basic_OP::has_identity() const { 35 bool basic_OP::has_identity() const {
35 return !identity.empty(); 36 return !identity.empty();
36 } 37 }
37 const string& basic_OP::get_claimed_id() const { 38 const string& basic_OP::get_claimed_id() const {
38 if(claimed_id.empty()) 39 if(claimed_id.empty())
39 throw non_identity(OPKELE_CP_ "attempting to retrieve claimed_id of non-identity related request"); 40 throw non_identity(OPKELE_CP_ "attempting to retrieve claimed_id of non-identity related request");
40 assert(!identity.empty()); 41 assert(!identity.empty());
41 return claimed_id; 42 return claimed_id;
42 } 43 }
43 const string& basic_OP::get_identity() const { 44 const string& basic_OP::get_identity() const {
44 if(identity.empty()) 45 if(identity.empty())
45 throw non_identity(OPKELE_CP_ "attempting to retrieve identity of non-identity related request"); 46 throw non_identity(OPKELE_CP_ "attempting to retrieve identity of non-identity related request");
46 assert(!claimed_id.empty()); 47 assert(!claimed_id.empty());
47 return identity; 48 return identity;
48 } 49 }
49 50
50 bool basic_OP::is_id_select() const { 51 bool basic_OP::is_id_select() const {
51 return identity==IDURI_SELECT20; 52 return identity==IDURI_SELECT20;
52 } 53 }
53 54
54 void basic_OP::select_identity(const string& c,const string& i) { 55 void basic_OP::select_identity(const string& c,const string& i) {
55 claimed_id = c; identity = i; 56 claimed_id = c; identity = i;
56 } 57 }
57 void basic_OP::set_claimed_id(const string& c) { 58 void basic_OP::set_claimed_id(const string& c) {
58 claimed_id = c; 59 claimed_id = c;
59 } 60 }
60 61
61 basic_openid_message& basic_OP::associate( 62 basic_openid_message& basic_OP::associate(
62 basic_openid_message& oum, 63 basic_openid_message& oum,
63 const basic_openid_message& inm) try { 64 const basic_openid_message& inm) try {
64 assert(inm.get_field("mode")=="associate"); 65 assert(inm.get_field("mode")=="associate");
65 util::dh_t dh; 66 util::dh_t dh;
66 util::bignum_t c_pub; 67 util::bignum_t c_pub;
67 unsigned char key_digest[SHA256_DIGEST_LENGTH]; 68 unsigned char key_digest[SHA256_DIGEST_LENGTH];
68 size_t d_len = 0; 69 size_t d_len = 0;
69 string sts = inm.get_field("session_type"); 70 string sts = inm.get_field("session_type");
70 string ats = inm.get_field("assoc_type"); 71 string ats = inm.get_field("assoc_type");
71 if(sts=="DH-SHA1" || sts=="DH-SHA256") { 72 if(sts=="DH-SHA1" || sts=="DH-SHA256") {
72 if(!(dh = DH_new())) 73 if(!(dh = DH_new()))
73 throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); 74 throw exception_openssl(OPKELE_CP_ "failed to DH_new()");
74 c_pub = util::base64_to_bignum(inm.get_field("dh_consumer_public")); 75 c_pub = util::base64_to_bignum(inm.get_field("dh_consumer_public"));
75 try { dh->p = util::base64_to_bignum(inm.get_field("dh_modulus")); 76 try { dh->p = util::base64_to_bignum(inm.get_field("dh_modulus"));
76 }catch(failed_lookup&) { 77 }catch(failed_lookup&) {
77 dh->p = util::dec_to_bignum(data::_default_p); } 78 dh->p = util::dec_to_bignum(data::_default_p); }
78 try { dh->g = util::base64_to_bignum(inm.get_field("dh_gen")); 79 try { dh->g = util::base64_to_bignum(inm.get_field("dh_gen"));
79 }catch(failed_lookup&) { 80 }catch(failed_lookup&) {
80 dh->g = util::dec_to_bignum(data::_default_g); } 81 dh->g = util::dec_to_bignum(data::_default_g); }
81 if(!DH_generate_key(dh)) 82 if(!DH_generate_key(dh))
82 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); 83 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()");
83 vector<unsigned char> ck(DH_size(dh)+1); 84 vector<unsigned char> ck(DH_size(dh)+1);
84 unsigned char *ckptr = &(ck.front())+1; 85 unsigned char *ckptr = &(ck.front())+1;
85 int cklen = DH_compute_key(ckptr,c_pub,dh); 86 int cklen = DH_compute_key(ckptr,c_pub,dh);
86 if(cklen<0) 87 if(cklen<0)
87 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); 88 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()");
88 if(cklen && (*ckptr)&0x80) { 89 if(cklen && (*ckptr)&0x80) {
89 (*(--ckptr)) = 0; ++cklen; } 90 (*(--ckptr)) = 0; ++cklen; }
90 if(sts=="DH-SHA1") { 91 if(sts=="DH-SHA1") {
91 SHA1(ckptr,cklen,key_digest); d_len = SHA_DIGEST_LENGTH; 92 SHA1(ckptr,cklen,key_digest); d_len = SHA_DIGEST_LENGTH;
92 }else if(sts=="DH-SHA256") { 93 }else if(sts=="DH-SHA256") {
93 SHA256(ckptr,cklen,key_digest); d_len = SHA256_DIGEST_LENGTH; 94 SHA256(ckptr,cklen,key_digest); d_len = SHA256_DIGEST_LENGTH;
94 }else 95 }else
95 throw internal_error(OPKELE_CP_ "I thought I knew the session type"); 96 throw internal_error(OPKELE_CP_ "I thought I knew the session type");
96 }else 97 }else
97 throw unsupported(OPKELE_CP_ "Unsupported session_type"); 98 throw unsupported(OPKELE_CP_ "Unsupported session_type");
98 assoc_t a; 99 assoc_t a;
99 if(ats=="HMAC-SHA1") 100 if(ats=="HMAC-SHA1")
100 a = alloc_assoc(ats,SHA_DIGEST_LENGTH,false); 101 a = alloc_assoc(ats,SHA_DIGEST_LENGTH,false);
101 else if(ats=="HMAC-SHA256") 102 else if(ats=="HMAC-SHA256")
102 a = alloc_assoc(ats,SHA256_DIGEST_LENGTH,false); 103 a = alloc_assoc(ats,SHA256_DIGEST_LENGTH,false);
103 else 104 else
104 throw unsupported(OPKELE_CP_ "Unsupported assoc_type"); 105 throw unsupported(OPKELE_CP_ "Unsupported assoc_type");
105 oum.reset_fields(); 106 oum.reset_fields();
106 oum.set_field("ns",OIURI_OPENID20); 107 oum.set_field("ns",OIURI_OPENID20);
107 oum.set_field("assoc_type",a->assoc_type()); 108 oum.set_field("assoc_type",a->assoc_type());
108 oum.set_field("assoc_handle",a->handle()); 109 oum.set_field("assoc_handle",a->handle());
109 oum.set_field("expires_in",util::long_to_string(a->expires_in())); 110 oum.set_field("expires_in",util::long_to_string(a->expires_in()));
110 secret_t secret = a->secret(); 111 secret_t secret = a->secret();
111 if(sts=="DH-SHA1" || sts=="DH-SHA256") { 112 if(sts=="DH-SHA1" || sts=="DH-SHA256") {
112 if(d_len != secret.size()) 113 if(d_len != secret.size())
113 throw bad_input(OPKELE_CP_ "Association secret and session MAC are not of the same size"); 114 throw bad_input(OPKELE_CP_ "Association secret and session MAC are not of the same size");
114 oum.set_field("session_type",sts); 115 oum.set_field("session_type",sts);
115 oum.set_field("dh_server_public",util::bignum_to_base64(dh->pub_key)); 116 oum.set_field("dh_server_public",util::bignum_to_base64(dh->pub_key));
116 string b64; secret.enxor_to_base64(key_digest,b64); 117 string b64; secret.enxor_to_base64(key_digest,b64);
117 oum.set_field("enc_mac_key",b64); 118 oum.set_field("enc_mac_key",b64);
118 }else /* TODO: support cleartext over encrypted connection */ 119 }else /* TODO: support cleartext over encrypted connection */
119 throw unsupported(OPKELE_CP_ "Unsupported session type"); 120 throw unsupported(OPKELE_CP_ "Unsupported session type");
120 return oum; 121 return oum;
121 } catch(unsupported& u) { 122 } catch(unsupported& u) {
122 oum.reset_fields(); 123 oum.reset_fields();
123 oum.set_field("ns",OIURI_OPENID20); 124 oum.set_field("ns",OIURI_OPENID20);
124 oum.set_field("error",u.what()); 125 oum.set_field("error",u.what());
125 oum.set_field("error_code","unsupported-type"); 126 oum.set_field("error_code","unsupported-type");
126 oum.set_field("session_type","DH-SHA256"); 127 oum.set_field("session_type","DH-SHA256");
127 oum.set_field("assoc_type","HMAC-SHA256"); 128 oum.set_field("assoc_type","HMAC-SHA256");
128 return oum; 129 return oum;
129 } 130 }
130 131
131 void basic_OP::checkid_(const basic_openid_message& inm, 132 void basic_OP::checkid_(const basic_openid_message& inm,
132 extension_t *ext) { 133 extension_t *ext) {
133 reset_vars(); 134 reset_vars();
134 string modestr = inm.get_field("mode"); 135 string modestr = inm.get_field("mode");
135 if(modestr=="checkid_setup") 136 if(modestr=="checkid_setup")
136 mode = mode_checkid_setup; 137 mode = mode_checkid_setup;
137 else if(modestr=="checkid_immediate") 138 else if(modestr=="checkid_immediate")
138 mode = mode_checkid_immediate; 139 mode = mode_checkid_immediate;
139 else 140 else
140 throw bad_input(OPKELE_CP_ "Invalid checkid_* mode"); 141 throw bad_input(OPKELE_CP_ "Invalid checkid_* mode");
141 try { 142 try {
142 assoc = retrieve_assoc(invalidate_handle=inm.get_field("assoc_handle")); 143 assoc = retrieve_assoc(invalidate_handle=inm.get_field("assoc_handle"));
143 invalidate_handle.clear(); 144 invalidate_handle.clear();
144 }catch(failed_lookup&) { } 145 }catch(failed_lookup&) { }
145 try { 146 try {
146 openid2 = (inm.get_field("ns")==OIURI_OPENID20); 147 openid2 = (inm.get_field("ns")==OIURI_OPENID20);
147 }catch(failed_lookup&) { openid2 = false; } 148 }catch(failed_lookup&) { openid2 = false; }
148 try { 149 try {
149 return_to = inm.get_field("return_to"); 150 return_to = inm.get_field("return_to");
150 }catch(failed_lookup&) { } 151 }catch(failed_lookup&) { }
151 if(openid2) { 152 if(openid2) {
152 try { 153 try {
153 realm = inm.get_field("realm"); 154 realm = inm.get_field("realm");
154 }catch(failed_lookup&) { 155 }catch(failed_lookup&) {
155 try { 156 try {
156 realm = inm.get_field("trust_root"); 157 realm = inm.get_field("trust_root");
157 }catch(failed_lookup&) { 158 }catch(failed_lookup&) {
158 if(return_to.empty()) 159 if(return_to.empty())
159 throw bad_input(OPKELE_CP_ 160 throw bad_input(OPKELE_CP_
160 "Both realm and return_to are unset"); 161 "Both realm and return_to are unset");
161 realm = return_to; 162 realm = return_to;
162 } 163 }
163 } 164 }
164 }else{ 165 }else{
165 try { 166 try {
166 realm = inm.get_field("trust_root"); 167 realm = inm.get_field("trust_root");
167 }catch(failed_lookup&) { 168 }catch(failed_lookup&) {
168 if(return_to.empty()) 169 if(return_to.empty())
169 throw bad_input(OPKELE_CP_ 170 throw bad_input(OPKELE_CP_
170 "Both realm and return_to are unset"); 171 "Both realm and return_to are unset");
171 realm = return_to; 172 realm = return_to;
172 } 173 }
173 } 174 }
174 try { 175 try {
175 identity = inm.get_field("identity"); 176 identity = inm.get_field("identity");
176 try { 177 try {
177 claimed_id = inm.get_field("claimed_id"); 178 claimed_id = inm.get_field("claimed_id");
178 }catch(failed_lookup&) { 179 }catch(failed_lookup&) {
179 if(openid2) 180 if(openid2)
180 throw bad_input(OPKELE_CP_ 181 throw bad_input(OPKELE_CP_
181 "claimed_id and identity must be either both present or both absent"); 182 "claimed_id and identity must be either both present or both absent");
182 claimed_id = identity; 183 claimed_id = identity;
183 } 184 }
184 }catch(failed_lookup&) { 185 }catch(failed_lookup&) {
185 if(openid2 && inm.has_field("claimed_id")) 186 if(openid2 && inm.has_field("claimed_id"))
186 throw bad_input(OPKELE_CP_ 187 throw bad_input(OPKELE_CP_
187 "claimed_id and identity must be either both present or both absent"); 188 "claimed_id and identity must be either both present or both absent");
188 } 189 }
189 verify_return_to(); 190 verify_return_to();
190 if(ext) ext->op_checkid_hook(inm); 191 if(ext) ext->op_checkid_hook(inm);
191 } 192 }
192 193
193 basic_openid_message& basic_OP::id_res(basic_openid_message& om, 194 basic_openid_message& basic_OP::id_res(basic_openid_message& om,
194 extension_t *ext) { 195 extension_t *ext) {
195 assert(!return_to.empty()); 196 assert(!return_to.empty());
196 assert(!is_id_select()); 197 assert(!is_id_select());
197 if(!assoc) { 198 if(!assoc) {
198 assoc = alloc_assoc("HMAC-SHA256",SHA256_DIGEST_LENGTH,true); 199 assoc = alloc_assoc("HMAC-SHA256",SHA256_DIGEST_LENGTH,true);
199 } 200 }
200 time_t now = time(0); 201 time_t now = time(0);
201 struct tm gmt; gmtime_r(&now,&gmt); 202 struct tm gmt; gmtime_r(&now,&gmt);
202 char w3timestr[24]; 203 char w3timestr[24];
203 if(!strftime(w3timestr,sizeof(w3timestr),"%Y-%m-%dT%H:%M:%SZ",&gmt)) 204 if(!strftime(w3timestr,sizeof(w3timestr),"%Y-%m-%dT%H:%M:%SZ",&gmt))
204 throw failed_conversion(OPKELE_CP_ 205 throw failed_conversion(OPKELE_CP_
205 "Failed to build time string for nonce" ); 206 "Failed to build time string for nonce" );
206 om.set_field("ns",OIURI_OPENID20); 207 om.set_field("ns",OIURI_OPENID20);
207 om.set_field("mode","id_res"); 208 om.set_field("mode","id_res");
208 om.set_field("op_endpoint",get_op_endpoint()); 209 om.set_field("op_endpoint",get_op_endpoint());
209 string ats = "ns,mode,op_endpoint,return_to,response_nonce," 210 string ats = "ns,mode,op_endpoint,return_to,response_nonce,"
210 "assoc_handle,signed"; 211 "assoc_handle,signed";
211 if(!identity.empty()) { 212 if(!identity.empty()) {
212 om.set_field("identity",identity); 213 om.set_field("identity",identity);
213 om.set_field("claimed_id",claimed_id); 214 om.set_field("claimed_id",claimed_id);
214 ats += ",identity,claimed_id"; 215 ats += ",identity,claimed_id";
215 } 216 }
216 om.set_field("return_to",return_to); 217 om.set_field("return_to",return_to);
217 string nonce = w3timestr; 218 string nonce = w3timestr;
218 om.set_field("response_nonce",alloc_nonce(nonce)); 219 om.set_field("response_nonce",alloc_nonce(nonce));
219 if(!invalidate_handle.empty()) { 220 if(!invalidate_handle.empty()) {
220 om.set_field("invalidate_handle",invalidate_handle); 221 om.set_field("invalidate_handle",invalidate_handle);
221 ats += ",invalidate_handle"; 222 ats += ",invalidate_handle";
222 } 223 }
223 om.set_field("assoc_handle",assoc->handle()); 224 om.set_field("assoc_handle",assoc->handle());
224 om.add_to_signed(ats); 225 om.add_to_signed(ats);
225 if(ext) ext->op_id_res_hook(om); 226 if(ext) ext->op_id_res_hook(om);
226 om.set_field("sig",util::base64_signature(assoc,om)); 227 om.set_field("sig",util::base64_signature(assoc,om));
227 return om; 228 return om;
228 } 229 }
229 230
230 basic_openid_message& basic_OP::cancel(basic_openid_message& om) { 231 basic_openid_message& basic_OP::cancel(basic_openid_message& om) {
231 assert(!return_to.empty()); 232 assert(!return_to.empty());
232 om.set_field("ns",OIURI_OPENID20); 233 om.set_field("ns",OIURI_OPENID20);
233 om.set_field("mode","cancel"); 234 om.set_field("mode","cancel");
234 return om; 235 return om;
235 } 236 }
236 237
237 basic_openid_message& basic_OP::error(basic_openid_message& om, 238 basic_openid_message& basic_OP::error(basic_openid_message& om,
238 const string& err,const string& contact, 239 const string& err,const string& contact,
239 const string& reference ) { 240 const string& reference ) {
240 assert(!return_to.empty()); 241 assert(!return_to.empty());
241 om.set_field("ns",OIURI_OPENID20); 242 om.set_field("ns",OIURI_OPENID20);
242 om.set_field("mode","error"); 243 om.set_field("mode","error");
243 om.set_field("error",err); 244 om.set_field("error",err);
244 if(!contact.empty()) om.set_field("contact",contact); 245 if(!contact.empty()) om.set_field("contact",contact);
245 if(!reference.empty()) om.set_field("reference",reference); 246 if(!reference.empty()) om.set_field("reference",reference);
246 return om; 247 return om;
247 } 248 }
248 249
249 basic_openid_message& basic_OP::setup_needed( 250 basic_openid_message& basic_OP::setup_needed(
250 basic_openid_message& oum,const basic_openid_message& inm) { 251 basic_openid_message& oum,const basic_openid_message& inm) {
251 assert(mode==mode_checkid_immediate); 252 assert(mode==mode_checkid_immediate);
252 assert(!return_to.empty()); 253 assert(!return_to.empty());
253 if(openid2) { 254 if(openid2) {
254 oum.set_field("ns",OIURI_OPENID20); 255 oum.set_field("ns",OIURI_OPENID20);
255 oum.set_field("mode","setup_needed"); 256 oum.set_field("mode","setup_needed");
256 }else{ 257 }else{
257 oum.set_field("mode","id_res"); 258 oum.set_field("mode","id_res");
258 static const string setupmode = "checkid_setup"; 259 static const string setupmode = "checkid_setup";
259 oum.set_field("user_setup_url", 260 oum.set_field("user_setup_url",
260 util::change_mode_message_proxy(inm,setupmode) 261 util::change_mode_message_proxy(inm,setupmode)
261 .append_query(get_op_endpoint())); 262 .append_query(get_op_endpoint()));
262 } 263 }
263 return oum; 264 return oum;
264 } 265 }
265 266
266 basic_openid_message& basic_OP::check_authentication( 267 basic_openid_message& basic_OP::check_authentication(
267 basic_openid_message& oum, 268 basic_openid_message& oum,
268 const basic_openid_message& inm) try { 269 const basic_openid_message& inm) try {
269 assert(inm.get_field("mode")=="check_authentication"); 270 assert(inm.get_field("mode")=="check_authentication");
270 oum.reset_fields(); 271 oum.reset_fields();
271 oum.set_field("ns",OIURI_OPENID20); 272 oum.set_field("ns",OIURI_OPENID20);
272 bool o2; 273 bool o2;
273 try { 274 try {
274 o2 = (inm.get_field("ns")==OIURI_OPENID20); 275 o2 = (inm.get_field("ns")==OIURI_OPENID20);
275 }catch(failed_lookup&) { o2 = false; } 276 }catch(failed_lookup&) { o2 = false; }
276 string nonce; 277 string nonce;
277 if(o2) { 278 if(o2) {
278 try { 279 try {
279 if(!check_nonce(nonce = inm.get_field("response_nonce"))) 280 if(!check_nonce(nonce = inm.get_field("response_nonce")))
280 throw failed_check_authentication(OPKELE_CP_ "Invalid nonce"); 281 throw failed_check_authentication(OPKELE_CP_ "Invalid nonce");
281 }catch(failed_lookup&) { 282 }catch(failed_lookup&) {
282 throw failed_check_authentication(OPKELE_CP_ "No nonce provided with check_authentication request"); 283 throw failed_check_authentication(OPKELE_CP_ "No nonce provided with check_authentication request");
283 } 284 }
284 } 285 }
285 try { 286 try {
286 assoc = retrieve_assoc(inm.get_field("assoc_handle")); 287 assoc = retrieve_assoc(inm.get_field("assoc_handle"));
287 if(!assoc->stateless()) 288 if(!assoc->stateless())
288 throw failed_check_authentication(OPKELE_CP_ "Will not do check_authentication on a stateful handle"); 289 throw failed_check_authentication(OPKELE_CP_ "Will not do check_authentication on a stateful handle");
289 }catch(failed_lookup&) { 290 }catch(failed_lookup&) {
290 throw failed_check_authentication(OPKELE_CP_ "No assoc_handle or invalid assoc_handle specified with check_authentication request"); 291 throw failed_check_authentication(OPKELE_CP_ "No assoc_handle or invalid assoc_handle specified with check_authentication request");
291 } 292 }
292 static const string idresmode = "id_res"; 293 static const string idresmode = "id_res";
293 try { 294 try {
294 if(util::base64_signature(assoc,util::change_mode_message_proxy(inm,idresmode))!=inm.get_field("sig")) 295 if(util::base64_signature(assoc,util::change_mode_message_proxy(inm,idresmode))!=inm.get_field("sig"))
295 throw failed_check_authentication(OPKELE_CP_ "Signature mismatch"); 296 throw failed_check_authentication(OPKELE_CP_ "Signature mismatch");
296 }catch(failed_lookup&) { 297 }catch(failed_lookup&) {
297 throw failed_check_authentication(OPKELE_CP_ "failed to calculate signature"); 298 throw failed_check_authentication(OPKELE_CP_ "failed to calculate signature");
298 } 299 }
299 oum.set_field("is_valid","true"); 300 oum.set_field("is_valid","true");
300 try { 301 try {
301 string h = inm.get_field("invalidate_handle"); 302 string h = inm.get_field("invalidate_handle");
302 try { 303 try {
303 assoc_t ih = retrieve_assoc(h); 304 assoc_t ih = retrieve_assoc(h);
304 }catch(invalid_handle& ih) { 305 }catch(invalid_handle& ih) {
305 oum.set_field("invalidate_handle",h); 306 oum.set_field("invalidate_handle",h);
306 }catch(failed_lookup& ih) { 307 }catch(failed_lookup& ih) {
307 oum.set_field("invalidate_handle",h); 308 oum.set_field("invalidate_handle",h);
308 } 309 }
309 }catch(failed_lookup&) { } 310 }catch(failed_lookup&) { }
310 if(o2) { 311 if(o2) {
311 assert(!nonce.empty()); 312 assert(!nonce.empty());
312 invalidate_nonce(nonce); 313 invalidate_nonce(nonce);
313 } 314 }
314 return oum; 315 return oum;
315 }catch(failed_check_authentication& ) { 316 }catch(failed_check_authentication& ) {
316 oum.set_field("is_valid","false"); 317 oum.set_field("is_valid","false");
317 return oum; 318 return oum;
318 } 319 }
319 320
320 void basic_OP::verify_return_to() { 321 void basic_OP::verify_return_to() {
321 if(realm.find('#')!=string::npos) 322 if(realm.find('#')!=string::npos)
322 throw opkele::bad_realm(OPKELE_CP_ "authentication realm contains URI fragment"); 323 throw opkele::bad_realm(OPKELE_CP_ "authentication realm contains URI fragment");
323 if(!util::uri_matches_realm(return_to,realm)) 324 if(!util::uri_matches_realm(return_to,realm))
324 throw bad_return_to(OPKELE_CP_ "return_to URL doesn't match realm"); 325 throw bad_return_to(OPKELE_CP_ "return_to URL doesn't match realm");
325 } 326 }
326 327
327} 328}
diff --git a/lib/basic_rp.cc b/lib/basic_rp.cc
index a0ad130..e65d9fb 100644
--- a/lib/basic_rp.cc
+++ b/lib/basic_rp.cc
@@ -1,299 +1,300 @@
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/curl.h> 10#include <opkele/curl.h>
10 11
11namespace opkele { 12namespace opkele {
12 13
13 static void dh_get_secret( 14 static void dh_get_secret(
14 secret_t& secret, const basic_openid_message& om, 15 secret_t& secret, const basic_openid_message& om,
15 const char *exp_assoc, const char *exp_sess, 16 const char *exp_assoc, const char *exp_sess,
16 util::dh_t& dh, 17 util::dh_t& dh,
17 size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*), 18 size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*),
18 size_t exp_s_len) try { 19 size_t exp_s_len) try {
19 if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess) 20 if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess)
20 throw bad_input(OPKELE_CP_ "Unexpected associate response"); 21 throw bad_input(OPKELE_CP_ "Unexpected associate response");
21 util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public")); 22 util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public"));
22 vector<unsigned char> ck(DH_size(dh)+1); 23 vector<unsigned char> ck(DH_size(dh)+1);
23 unsigned char *ckptr = &(ck.front())+1; 24 unsigned char *ckptr = &(ck.front())+1;
24 int cklen = DH_compute_key(ckptr,s_pub,dh); 25 int cklen = DH_compute_key(ckptr,s_pub,dh);
25 if(cklen<0) 26 if(cklen<0)
26 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); 27 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()");
27 if(cklen && (*ckptr)&0x80) { 28 if(cklen && (*ckptr)&0x80) {
28 (*(--ckptr))=0; ++cklen; } 29 (*(--ckptr))=0; ++cklen; }
29 assert(d_len<=SHA256_DIGEST_LENGTH); 30 assert(d_len<=SHA256_DIGEST_LENGTH);
30 unsigned char key_digest[SHA256_DIGEST_LENGTH]; 31 unsigned char key_digest[SHA256_DIGEST_LENGTH];
31 secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key")); 32 secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key"));
32 if(secret.size()!=exp_s_len) 33 if(secret.size()!=exp_s_len)
33 throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type"); 34 throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type");
34 }catch(opkele::failed_lookup& ofl) { 35 }catch(opkele::failed_lookup& ofl) {
35 throw bad_input(OPKELE_CP_ "Incoherent response from OP"); 36 throw bad_input(OPKELE_CP_ "Incoherent response from OP");
36 } OPKELE_RETHROW 37 } OPKELE_RETHROW
37 38
38 static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) { 39 static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) {
39 util::curl_pick_t curl = util::curl_pick_t::easy_init(); 40 util::curl_pick_t curl = util::curl_pick_t::easy_init();
40 if(!curl) 41 if(!curl)
41 throw exception_curl(OPKELE_CP_ "failed to initialize curl"); 42 throw exception_curl(OPKELE_CP_ "failed to initialize curl");
42 string request = inm.query_string(); 43 string request = inm.query_string();
43 CURLcode r; 44 CURLcode r;
44 (r=curl.misc_sets()) 45 (r=curl.misc_sets())
45 || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str())) 46 || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str()))
46 || (r=curl.easy_setopt(CURLOPT_POST,1)) 47 || (r=curl.easy_setopt(CURLOPT_POST,1))
47 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) 48 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data()))
48 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) 49 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length()))
49 || (r=curl.set_write()); 50 || (r=curl.set_write());
50 if(r) 51 if(r)
51 throw exception_curl(OPKELE_CP_ "failed to set curly options",r); 52 throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
52 if( (r=curl.easy_perform()) ) 53 if( (r=curl.easy_perform()) )
53 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); 54 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
54 oum.from_keyvalues(curl.response); 55 oum.from_keyvalues(curl.response);
55 } 56 }
56 57
57 58
58 assoc_t basic_RP::associate(const string& OP) { 59 assoc_t basic_RP::associate(const string& OP) {
59 util::dh_t dh = DH_new(); 60 util::dh_t dh = DH_new();
60 if(!dh) 61 if(!dh)
61 throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); 62 throw exception_openssl(OPKELE_CP_ "failed to DH_new()");
62 dh->p = util::dec_to_bignum(data::_default_p); 63 dh->p = util::dec_to_bignum(data::_default_p);
63 dh->g = util::dec_to_bignum(data::_default_g); 64 dh->g = util::dec_to_bignum(data::_default_g);
64 if(!DH_generate_key(dh)) 65 if(!DH_generate_key(dh))
65 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); 66 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()");
66 openid_message_t req; 67 openid_message_t req;
67 req.set_field("ns",OIURI_OPENID20); 68 req.set_field("ns",OIURI_OPENID20);
68 req.set_field("mode","associate"); 69 req.set_field("mode","associate");
69 req.set_field("dh_modulus",util::bignum_to_base64(dh->p)); 70 req.set_field("dh_modulus",util::bignum_to_base64(dh->p));
70 req.set_field("dh_gen",util::bignum_to_base64(dh->g)); 71 req.set_field("dh_gen",util::bignum_to_base64(dh->g));
71 req.set_field("dh_consumer_public",util::bignum_to_base64(dh->pub_key)); 72 req.set_field("dh_consumer_public",util::bignum_to_base64(dh->pub_key));
72 openid_message_t res; 73 openid_message_t res;
73 req.set_field("assoc_type","HMAC-SHA256"); 74 req.set_field("assoc_type","HMAC-SHA256");
74 req.set_field("session_type","DH-SHA256"); 75 req.set_field("session_type","DH-SHA256");
75 secret_t secret; 76 secret_t secret;
76 int expires_in; 77 int expires_in;
77 try { 78 try {
78 direct_request(res,req,OP); 79 direct_request(res,req,OP);
79 dh_get_secret( secret, res, 80 dh_get_secret( secret, res,
80 "HMAC-SHA256", "DH-SHA256", 81 "HMAC-SHA256", "DH-SHA256",
81 dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH ); 82 dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH );
82 expires_in = util::string_to_long(res.get_field("expires_in")); 83 expires_in = util::string_to_long(res.get_field("expires_in"));
83 }catch(exception&) { 84 }catch(exception&) {
84 try { 85 try {
85 req.set_field("assoc_type","HMAC-SHA1"); 86 req.set_field("assoc_type","HMAC-SHA1");
86 req.set_field("session_type","DH-SHA1"); 87 req.set_field("session_type","DH-SHA1");
87 direct_request(res,req,OP); 88 direct_request(res,req,OP);
88 dh_get_secret( secret, res, 89 dh_get_secret( secret, res,
89 "HMAC-SHA1", "DH-SHA1", 90 "HMAC-SHA1", "DH-SHA1",
90 dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH ); 91 dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH );
91 expires_in = util::string_to_long(res.get_field("expires_in")); 92 expires_in = util::string_to_long(res.get_field("expires_in"));
92 }catch(bad_input&) { 93 }catch(bad_input&) {
93 throw dumb_RP(OPKELE_CP_ "OP failed to supply an association"); 94 throw dumb_RP(OPKELE_CP_ "OP failed to supply an association");
94 } 95 }
95 } 96 }
96 return store_assoc( 97 return store_assoc(
97 OP, res.get_field("assoc_handle"), 98 OP, res.get_field("assoc_handle"),
98 res.get_field("assoc_type"), secret, 99 res.get_field("assoc_type"), secret,
99 expires_in ); 100 expires_in );
100 } 101 }
101 102
102 basic_openid_message& basic_RP::checkid_( 103 basic_openid_message& basic_RP::checkid_(
103 basic_openid_message& rv, 104 basic_openid_message& rv,
104 mode_t mode, 105 mode_t mode,
105 const string& return_to,const string& realm, 106 const string& return_to,const string& realm,
106 extension_t *ext) { 107 extension_t *ext) {
107 rv.reset_fields(); 108 rv.reset_fields();
108 rv.set_field("ns",OIURI_OPENID20); 109 rv.set_field("ns",OIURI_OPENID20);
109 if(mode==mode_checkid_immediate) 110 if(mode==mode_checkid_immediate)
110 rv.set_field("mode","checkid_immediate"); 111 rv.set_field("mode","checkid_immediate");
111 else if(mode==mode_checkid_setup) 112 else if(mode==mode_checkid_setup)
112 rv.set_field("mode","checkid_setup"); 113 rv.set_field("mode","checkid_setup");
113 else 114 else
114 throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); 115 throw bad_input(OPKELE_CP_ "unknown checkid_* mode");
115 if(realm.empty() && return_to.empty()) 116 if(realm.empty() && return_to.empty())
116 throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty"); 117 throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty");
117 if(!realm.empty()) { 118 if(!realm.empty()) {
118 rv.set_field("realm",realm); 119 rv.set_field("realm",realm);
119 rv.set_field("trust_root",realm); 120 rv.set_field("trust_root",realm);
120 } 121 }
121 if(!return_to.empty()) 122 if(!return_to.empty())
122 rv.set_field("return_to",return_to); 123 rv.set_field("return_to",return_to);
123 const openid_endpoint_t& ep = get_endpoint(); 124 const openid_endpoint_t& ep = get_endpoint();
124 rv.set_field("claimed_id",ep.claimed_id); 125 rv.set_field("claimed_id",ep.claimed_id);
125 rv.set_field("identity",ep.local_id); 126 rv.set_field("identity",ep.local_id);
126 try { 127 try {
127 rv.set_field("assoc_handle",find_assoc(ep.uri)->handle()); 128 rv.set_field("assoc_handle",find_assoc(ep.uri)->handle());
128 }catch(dumb_RP& drp) { 129 }catch(dumb_RP& drp) {
129 }catch(failed_lookup& fl) { 130 }catch(failed_lookup& fl) {
130 try { 131 try {
131 rv.set_field("assoc_handle",associate(ep.uri)->handle()); 132 rv.set_field("assoc_handle",associate(ep.uri)->handle());
132 }catch(dumb_RP& drp) { } 133 }catch(dumb_RP& drp) { }
133 } OPKELE_RETHROW 134 } OPKELE_RETHROW
134 if(ext) ext->rp_checkid_hook(rv); 135 if(ext) ext->rp_checkid_hook(rv);
135 return rv; 136 return rv;
136 } 137 }
137 138
138 class signed_part_message_proxy : public basic_openid_message { 139 class signed_part_message_proxy : public basic_openid_message {
139 public: 140 public:
140 const basic_openid_message& x; 141 const basic_openid_message& x;
141 set<string> signeds; 142 set<string> signeds;
142 143
143 signed_part_message_proxy(const basic_openid_message& xx) : x(xx) { 144 signed_part_message_proxy(const basic_openid_message& xx) : x(xx) {
144 const string& slist = x.get_field("signed"); 145 const string& slist = x.get_field("signed");
145 string::size_type p = 0; 146 string::size_type p = 0;
146 while(true) { 147 while(true) {
147 string::size_type co = slist.find(',',p); 148 string::size_type co = slist.find(',',p);
148 string f = (co==string::npos) 149 string f = (co==string::npos)
149 ?slist.substr(p):slist.substr(p,co-p); 150 ?slist.substr(p):slist.substr(p,co-p);
150 signeds.insert(f); 151 signeds.insert(f);
151 if(co==string::npos) break; 152 if(co==string::npos) break;
152 p = co+1; 153 p = co+1;
153 } 154 }
154 } 155 }
155 156
156 bool has_field(const string& n) const { 157 bool has_field(const string& n) const {
157 return signeds.find(n)!=signeds.end() && x.has_field(n); } 158 return signeds.find(n)!=signeds.end() && x.has_field(n); }
158 const string& get_field(const string& n) const { 159 const string& get_field(const string& n) const {
159 if(signeds.find(n)==signeds.end()) 160 if(signeds.find(n)==signeds.end())
160 throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed"); 161 throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed");
161 return x.get_field(n); } 162 return x.get_field(n); }
162 163
163 fields_iterator fields_begin() const { 164 fields_iterator fields_begin() const {
164 return signeds.begin(); } 165 return signeds.begin(); }
165 fields_iterator fields_end() const { 166 fields_iterator fields_end() const {
166 return signeds.end(); } 167 return signeds.end(); }
167 }; 168 };
168 169
169 static void parse_query(const string& u,string::size_type q, 170 static void parse_query(const string& u,string::size_type q,
170 map<string,string>& p) { 171 map<string,string>& p) {
171 if(q==string::npos) 172 if(q==string::npos)
172 return; 173 return;
173 assert(u[q]=='?'); 174 assert(u[q]=='?');
174 ++q; 175 ++q;
175 string::size_type l = u.size(); 176 string::size_type l = u.size();
176 while(q<l) { 177 while(q<l) {
177 string::size_type eq = u.find('=',q); 178 string::size_type eq = u.find('=',q);
178 string::size_type am = u.find('&',q); 179 string::size_type am = u.find('&',q);
179 if(am==string::npos) { 180 if(am==string::npos) {
180 if(eq==string::npos) { 181 if(eq==string::npos) {
181 p[""] = u.substr(q); 182 p[""] = u.substr(q);
182 }else{ 183 }else{
183 p[u.substr(q,eq-q)] = u.substr(eq+1); 184 p[u.substr(q,eq-q)] = u.substr(eq+1);
184 } 185 }
185 break; 186 break;
186 }else{ 187 }else{
187 if(eq==string::npos || eq>am) { 188 if(eq==string::npos || eq>am) {
188 p[""] = u.substr(q,eq-q); 189 p[""] = u.substr(q,eq-q);
189 }else{ 190 }else{
190 p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1); 191 p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1);
191 } 192 }
192 q = ++am; 193 q = ++am;
193 } 194 }
194 } 195 }
195 } 196 }
196 197
197 void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) { 198 void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) {
198 bool o2 = om.has_field("ns") 199 bool o2 = om.has_field("ns")
199 && om.get_field("ns")==OIURI_OPENID20; 200 && om.get_field("ns")==OIURI_OPENID20;
200 if( (!o2) && om.has_field("user_setup_url")) 201 if( (!o2) && om.has_field("user_setup_url"))
201 throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided", 202 throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",
202 om.get_field("user_setup_url")); 203 om.get_field("user_setup_url"));
203 string m = om.get_field("mode"); 204 string m = om.get_field("mode");
204 if(o2 && m=="setup_needed") 205 if(o2 && m=="setup_needed")
205 throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided"); 206 throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided");
206 if(m=="cancel") 207 if(m=="cancel")
207 throw id_res_cancel(OPKELE_CP_ "authentication cancelled"); 208 throw id_res_cancel(OPKELE_CP_ "authentication cancelled");
208 bool go_dumb=false; 209 bool go_dumb=false;
209 try { 210 try {
210 string OP = o2 211 string OP = o2
211 ?om.get_field("op_endpoint") 212 ?om.get_field("op_endpoint")
212 :get_endpoint().uri; 213 :get_endpoint().uri;
213 assoc_t assoc = retrieve_assoc( 214 assoc_t assoc = retrieve_assoc(
214 OP,om.get_field("assoc_handle")); 215 OP,om.get_field("assoc_handle"));
215 if(om.get_field("sig")!=util::base64_signature(assoc,om)) 216 if(om.get_field("sig")!=util::base64_signature(assoc,om))
216 throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); 217 throw id_res_mismatch(OPKELE_CP_ "signature mismatch");
217 }catch(dumb_RP& drp) { 218 }catch(dumb_RP& drp) {
218 go_dumb=true; 219 go_dumb=true;
219 }catch(failed_lookup& e) { 220 }catch(failed_lookup& e) {
220 go_dumb=true; 221 go_dumb=true;
221 } OPKELE_RETHROW 222 } OPKELE_RETHROW
222 if(go_dumb) { 223 if(go_dumb) {
223 try { 224 try {
224 string OP = o2 225 string OP = o2
225 ?om.get_field("op_endpoint") 226 ?om.get_field("op_endpoint")
226 :get_endpoint().uri; 227 :get_endpoint().uri;
227 check_authentication(OP,om); 228 check_authentication(OP,om);
228 }catch(failed_check_authentication& fca) { 229 }catch(failed_check_authentication& fca) {
229 throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); 230 throw id_res_failed(OPKELE_CP_ "failed to check_authentication()");
230 } OPKELE_RETHROW 231 } OPKELE_RETHROW
231 } 232 }
232 signed_part_message_proxy signeds(om); 233 signed_part_message_proxy signeds(om);
233 if(o2) { 234 if(o2) {
234 check_nonce(om.get_field("op_endpoint"), 235 check_nonce(om.get_field("op_endpoint"),
235 om.get_field("response_nonce")); 236 om.get_field("response_nonce"));
236 static const char *mustsign[] = { 237 static const char *mustsign[] = {
237 "op_endpoint", "return_to", "response_nonce", "assoc_handle", 238 "op_endpoint", "return_to", "response_nonce", "assoc_handle",
238 "claimed_id", "identity" }; 239 "claimed_id", "identity" };
239 for(size_t ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) { 240 for(size_t ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) {
240 if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms])) 241 if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms]))
241 throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs"); 242 throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs");
242 } 243 }
243 if( ( 244 if( (
244 (om.has_field("claimed_id")?1:0) 245 (om.has_field("claimed_id")?1:0)
245 ^ 246 ^
246 (om.has_field("identity")?1:0) 247 (om.has_field("identity")?1:0)
247 )&1 ) 248 )&1 )
248 throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent"); 249 throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent");
249 250
250 string turl = util::rfc_3986_normalize_uri(get_this_url()); 251 string turl = util::rfc_3986_normalize_uri(get_this_url());
251 util::strip_uri_fragment_part(turl); 252 util::strip_uri_fragment_part(turl);
252 string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to")); 253 string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to"));
253 util::strip_uri_fragment_part(rurl); 254 util::strip_uri_fragment_part(rurl);
254 string::size_type 255 string::size_type
255 tq = turl.find('?'), rq = rurl.find('?'); 256 tq = turl.find('?'), rq = rurl.find('?');
256 if( 257 if(
257 ((tq==string::npos)?turl:turl.substr(0,tq)) 258 ((tq==string::npos)?turl:turl.substr(0,tq))
258 != 259 !=
259 ((rq==string::npos)?rurl:rurl.substr(0,rq)) 260 ((rq==string::npos)?rurl:rurl.substr(0,rq))
260 ) 261 )
261 throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url"); 262 throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url");
262 map<string,string> tp; parse_query(turl,tq,tp); 263 map<string,string> tp; parse_query(turl,tq,tp);
263 map<string,string> rp; parse_query(rurl,rq,rp); 264 map<string,string> rp; parse_query(rurl,rq,rp);
264 for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) { 265 for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) {
265 map<string,string>::const_iterator tpi = tp.find(rpi->first); 266 map<string,string>::const_iterator tpi = tp.find(rpi->first);
266 if(tpi==tp.end()) 267 if(tpi==tp.end())
267 throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request"); 268 throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request");
268 if(tpi->second!=rpi->second) 269 if(tpi->second!=rpi->second)
269 throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request"); 270 throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request");
270 } 271 }
271 272
272 if(om.has_field("claimed_id")) { 273 if(om.has_field("claimed_id")) {
273 verify_OP( 274 verify_OP(
274 om.get_field("op_endpoint"), 275 om.get_field("op_endpoint"),
275 om.get_field("claimed_id"), 276 om.get_field("claimed_id"),
276 om.get_field("identity") ); 277 om.get_field("identity") );
277 } 278 }
278 279
279 } 280 }
280 if(ext) ext->rp_id_res_hook(om,signeds); 281 if(ext) ext->rp_id_res_hook(om,signeds);
281 } 282 }
282 283
283 void basic_RP::check_authentication(const string& OP, 284 void basic_RP::check_authentication(const string& OP,
284 const basic_openid_message& om){ 285 const basic_openid_message& om){
285 openid_message_t res; 286 openid_message_t res;
286 static const string checkauthmode = "check_authentication"; 287 static const string checkauthmode = "check_authentication";
287 direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP); 288 direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP);
288 if(res.has_field("is_valid")) { 289 if(res.has_field("is_valid")) {
289 if(res.get_field("is_valid")=="true") { 290 if(res.get_field("is_valid")=="true") {
290 if(res.has_field("invalidate_handle")) 291 if(res.has_field("invalidate_handle"))
291 invalidate_assoc(OP,res.get_field("invalidate_handle")); 292 invalidate_assoc(OP,res.get_field("invalidate_handle"));
292 return; 293 return;
293 } 294 }
294 } 295 }
295 throw failed_check_authentication( 296 throw failed_check_authentication(
296 OPKELE_CP_ "failed to verify response"); 297 OPKELE_CP_ "failed to verify response");
297 } 298 }
298 299
299} 300}
diff --git a/lib/consumer.cc b/lib/consumer.cc
index ebda262..801496e 100644
--- a/lib/consumer.cc
+++ b/lib/consumer.cc
@@ -1,395 +1,396 @@
1#include <algorithm> 1#include <algorithm>
2#include <cassert> 2#include <cassert>
3#include <cstring> 3#include <cstring>
4#include <opkele/util.h> 4#include <opkele/util.h>
5#include <opkele/util-internal.h>
5#include <opkele/curl.h> 6#include <opkele/curl.h>
6#include <opkele/exception.h> 7#include <opkele/exception.h>
7#include <opkele/data.h> 8#include <opkele/data.h>
8#include <opkele/consumer.h> 9#include <opkele/consumer.h>
9#include <openssl/sha.h> 10#include <openssl/sha.h>
10#include <openssl/hmac.h> 11#include <openssl/hmac.h>
11#include <iostream> 12#include <iostream>
12 13
13#include "config.h" 14#include "config.h"
14 15
15#include <pcre.h> 16#include <pcre.h>
16 17
17namespace opkele { 18namespace opkele {
18 using namespace std; 19 using namespace std;
19 using util::curl_t; 20 using util::curl_t;
20 using util::curl_pick_t; 21 using util::curl_pick_t;
21 22
22 class pcre_matches_t { 23 class pcre_matches_t {
23 public: 24 public:
24 int *_ov; 25 int *_ov;
25 int _s; 26 int _s;
26 27
27 pcre_matches_t() : _ov(0), _s(0) { } 28 pcre_matches_t() : _ov(0), _s(0) { }
28 pcre_matches_t(int s) : _ov(0), _s(s) { 29 pcre_matches_t(int s) : _ov(0), _s(s) {
29 if(_s&1) ++_s; 30 if(_s&1) ++_s;
30 _s += _s>>1; 31 _s += _s>>1;
31 _ov = new int[_s]; 32 _ov = new int[_s];
32 } 33 }
33 ~pcre_matches_t() throw() { if(_ov) delete[] _ov; } 34 ~pcre_matches_t() throw() { if(_ov) delete[] _ov; }
34 35
35 int begin(int i) const { return _ov[i<<1]; } 36 int begin(int i) const { return _ov[i<<1]; }
36 int end(int i) const { return _ov[(i<<1)+1]; } 37 int end(int i) const { return _ov[(i<<1)+1]; }
37 int length(int i) const { int t=i<<1; return _ov[t+1]-_ov[t]; } 38 int length(int i) const { int t=i<<1; return _ov[t+1]-_ov[t]; }
38 }; 39 };
39 40
40 class pcre_t { 41 class pcre_t {
41 public: 42 public:
42 pcre *_p; 43 pcre *_p;
43 44
44 pcre_t() : _p(0) { } 45 pcre_t() : _p(0) { }
45 pcre_t(pcre *p) : _p(p) { } 46 pcre_t(pcre *p) : _p(p) { }
46 pcre_t(const char *re,int opts) : _p(0) { 47 pcre_t(const char *re,int opts) : _p(0) {
47 static const char *errptr; static int erroffset; 48 static const char *errptr; static int erroffset;
48 _p = pcre_compile(re,opts,&errptr,&erroffset,NULL); 49 _p = pcre_compile(re,opts,&errptr,&erroffset,NULL);
49 if(!_p) 50 if(!_p)
50 throw internal_error(OPKELE_CP_ string("Failed to compile regexp: ")+errptr); 51 throw internal_error(OPKELE_CP_ string("Failed to compile regexp: ")+errptr);
51 } 52 }
52 ~pcre_t() throw() { if(_p) (*pcre_free)(_p); } 53 ~pcre_t() throw() { if(_p) (*pcre_free)(_p); }
53 54
54 pcre_t& operator=(pcre *p) { if(_p) (*pcre_free)(_p); _p=p; return *this; } 55 pcre_t& operator=(pcre *p) { if(_p) (*pcre_free)(_p); _p=p; return *this; }
55 56
56 operator const pcre*(void) const { return _p; } 57 operator const pcre*(void) const { return _p; }
57 operator pcre*(void) { return _p; } 58 operator pcre*(void) { return _p; }
58 59
59 int exec(const string& s,pcre_matches_t& m) { 60 int exec(const string& s,pcre_matches_t& m) {
60 if(!_p) 61 if(!_p)
61 throw internal_error(OPKELE_CP_ "Trying to execute absent regexp"); 62 throw internal_error(OPKELE_CP_ "Trying to execute absent regexp");
62 return pcre_exec(_p,NULL,s.c_str(),s.length(),0,0,m._ov,m._s); 63 return pcre_exec(_p,NULL,s.c_str(),s.length(),0,0,m._ov,m._s);
63 } 64 }
64 }; 65 };
65 66
66 assoc_t consumer_t::associate(const string& server) { 67 assoc_t consumer_t::associate(const string& server) {
67 util::dh_t dh = DH_new(); 68 util::dh_t dh = DH_new();
68 if(!dh) 69 if(!dh)
69 throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); 70 throw exception_openssl(OPKELE_CP_ "failed to DH_new()");
70 dh->p = util::dec_to_bignum(data::_default_p); 71 dh->p = util::dec_to_bignum(data::_default_p);
71 dh->g = util::dec_to_bignum(data::_default_g); 72 dh->g = util::dec_to_bignum(data::_default_g);
72 if(!DH_generate_key(dh)) 73 if(!DH_generate_key(dh))
73 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); 74 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()");
74 string request = 75 string request =
75 "openid.mode=associate" 76 "openid.mode=associate"
76 "&openid.assoc_type=HMAC-SHA1" 77 "&openid.assoc_type=HMAC-SHA1"
77 "&openid.session_type=DH-SHA1" 78 "&openid.session_type=DH-SHA1"
78 "&openid.dh_consumer_public="; 79 "&openid.dh_consumer_public=";
79 request += util::url_encode(util::bignum_to_base64(dh->pub_key)); 80 request += util::url_encode(util::bignum_to_base64(dh->pub_key));
80 curl_pick_t curl = curl_pick_t::easy_init(); 81 curl_pick_t curl = curl_pick_t::easy_init();
81 if(!curl) 82 if(!curl)
82 throw exception_curl(OPKELE_CP_ "failed to initialize curl"); 83 throw exception_curl(OPKELE_CP_ "failed to initialize curl");
83 CURLcode r; 84 CURLcode r;
84 (r=curl.misc_sets()) 85 (r=curl.misc_sets())
85 || (r=curl.easy_setopt(CURLOPT_URL,server.c_str())) 86 || (r=curl.easy_setopt(CURLOPT_URL,server.c_str()))
86 || (r=curl.easy_setopt(CURLOPT_POST,1)) 87 || (r=curl.easy_setopt(CURLOPT_POST,1))
87 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) 88 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data()))
88 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) 89 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length()))
89 || (r=curl.set_write()) 90 || (r=curl.set_write())
90 ; 91 ;
91 if(r) 92 if(r)
92 throw exception_curl(OPKELE_CP_ "failed to set curly options",r); 93 throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
93 if( (r=curl.easy_perform()) ) 94 if( (r=curl.easy_perform()) )
94 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); 95 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
95 params_t p; p.parse_keyvalues(curl.response); 96 params_t p; p.parse_keyvalues(curl.response);
96 if(p.has_param("assoc_type") && p.get_param("assoc_type")!="HMAC-SHA1") 97 if(p.has_param("assoc_type") && p.get_param("assoc_type")!="HMAC-SHA1")
97 throw bad_input(OPKELE_CP_ "unsupported assoc_type"); 98 throw bad_input(OPKELE_CP_ "unsupported assoc_type");
98 string st; 99 string st;
99 if(p.has_param("session_type")) st = p.get_param("session_type"); 100 if(p.has_param("session_type")) st = p.get_param("session_type");
100 if((!st.empty()) && st!="DH-SHA1") 101 if((!st.empty()) && st!="DH-SHA1")
101 throw bad_input(OPKELE_CP_ "unsupported session_type"); 102 throw bad_input(OPKELE_CP_ "unsupported session_type");
102 secret_t secret; 103 secret_t secret;
103 if(st.empty()) { 104 if(st.empty()) {
104 secret.from_base64(p.get_param("mac_key")); 105 secret.from_base64(p.get_param("mac_key"));
105 }else{ 106 }else{
106 util::bignum_t s_pub = util::base64_to_bignum(p.get_param("dh_server_public")); 107 util::bignum_t s_pub = util::base64_to_bignum(p.get_param("dh_server_public"));
107 vector<unsigned char> ck(DH_size(dh)+1); 108 vector<unsigned char> ck(DH_size(dh)+1);
108 unsigned char *ckptr = &(ck.front())+1; 109 unsigned char *ckptr = &(ck.front())+1;
109 int cklen = DH_compute_key(ckptr,s_pub,dh); 110 int cklen = DH_compute_key(ckptr,s_pub,dh);
110 if(cklen<0) 111 if(cklen<0)
111 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); 112 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()");
112 if(cklen && (*ckptr)&0x80) { 113 if(cklen && (*ckptr)&0x80) {
113 (*(--ckptr)) = 0; ++cklen; 114 (*(--ckptr)) = 0; ++cklen;
114 } 115 }
115 unsigned char key_sha1[SHA_DIGEST_LENGTH]; 116 unsigned char key_sha1[SHA_DIGEST_LENGTH];
116 SHA1(ckptr,cklen,key_sha1); 117 SHA1(ckptr,cklen,key_sha1);
117 secret.enxor_from_base64(key_sha1,p.get_param("enc_mac_key")); 118 secret.enxor_from_base64(key_sha1,p.get_param("enc_mac_key"));
118 } 119 }
119 int expires_in = 0; 120 int expires_in = 0;
120 if(p.has_param("expires_in")) { 121 if(p.has_param("expires_in")) {
121 expires_in = util::string_to_long(p.get_param("expires_in")); 122 expires_in = util::string_to_long(p.get_param("expires_in"));
122 }else if(p.has_param("issued") && p.has_param("expiry")) { 123 }else if(p.has_param("issued") && p.has_param("expiry")) {
123 expires_in = util::w3c_to_time(p.get_param("expiry"))-util::w3c_to_time(p.get_param("issued")); 124 expires_in = util::w3c_to_time(p.get_param("expiry"))-util::w3c_to_time(p.get_param("issued"));
124 }else 125 }else
125 throw bad_input(OPKELE_CP_ "no expiration information"); 126 throw bad_input(OPKELE_CP_ "no expiration information");
126 return store_assoc(server,p.get_param("assoc_handle"),secret,expires_in); 127 return store_assoc(server,p.get_param("assoc_handle"),secret,expires_in);
127 } 128 }
128 129
129 string consumer_t::checkid_immediate(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { 130 string consumer_t::checkid_immediate(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) {
130 return checkid_(mode_checkid_immediate,identity,return_to,trust_root,ext); 131 return checkid_(mode_checkid_immediate,identity,return_to,trust_root,ext);
131 } 132 }
132 string consumer_t::checkid_setup(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { 133 string consumer_t::checkid_setup(const string& identity,const string& return_to,const string& trust_root,extension_t *ext) {
133 return checkid_(mode_checkid_setup,identity,return_to,trust_root,ext); 134 return checkid_(mode_checkid_setup,identity,return_to,trust_root,ext);
134 } 135 }
135 string consumer_t::checkid_(mode_t mode,const string& identity,const string& return_to,const string& trust_root,extension_t *ext) { 136 string consumer_t::checkid_(mode_t mode,const string& identity,const string& return_to,const string& trust_root,extension_t *ext) {
136 params_t p; 137 params_t p;
137 if(mode==mode_checkid_immediate) 138 if(mode==mode_checkid_immediate)
138 p["mode"]="checkid_immediate"; 139 p["mode"]="checkid_immediate";
139 else if(mode==mode_checkid_setup) 140 else if(mode==mode_checkid_setup)
140 p["mode"]="checkid_setup"; 141 p["mode"]="checkid_setup";
141 else 142 else
142 throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); 143 throw bad_input(OPKELE_CP_ "unknown checkid_* mode");
143 string iurl = canonicalize(identity); 144 string iurl = canonicalize(identity);
144 string server, delegate; 145 string server, delegate;
145 retrieve_links(iurl,server,delegate); 146 retrieve_links(iurl,server,delegate);
146 p["identity"] = delegate.empty()?iurl:delegate; 147 p["identity"] = delegate.empty()?iurl:delegate;
147 if(!trust_root.empty()) 148 if(!trust_root.empty())
148 p["trust_root"] = trust_root; 149 p["trust_root"] = trust_root;
149 p["return_to"] = return_to; 150 p["return_to"] = return_to;
150 try { 151 try {
151 string ah = find_assoc(server)->handle(); 152 string ah = find_assoc(server)->handle();
152 p["assoc_handle"] = ah; 153 p["assoc_handle"] = ah;
153 }catch(failed_lookup& fl) { 154 }catch(failed_lookup& fl) {
154 string ah = associate(server)->handle(); 155 string ah = associate(server)->handle();
155 p["assoc_handle"] = ah; 156 p["assoc_handle"] = ah;
156 } 157 }
157 if(ext) ext->checkid_hook(p); 158 if(ext) ext->checkid_hook(p);
158 return p.append_query(server); 159 return p.append_query(server);
159 } 160 }
160 161
161 void consumer_t::id_res(const params_t& pin,const string& identity,extension_t *ext) { 162 void consumer_t::id_res(const params_t& pin,const string& identity,extension_t *ext) {
162 if(pin.has_param("openid.user_setup_url")) 163 if(pin.has_param("openid.user_setup_url"))
163 throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",pin.get_param("openid.user_setup_url")); 164 throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided",pin.get_param("openid.user_setup_url"));
164 string server,delegate; 165 string server,delegate;
165 retrieve_links(identity.empty()?pin.get_param("openid.identity"):canonicalize(identity),server,delegate); 166 retrieve_links(identity.empty()?pin.get_param("openid.identity"):canonicalize(identity),server,delegate);
166 params_t ps; 167 params_t ps;
167 try { 168 try {
168 assoc_t assoc = retrieve_assoc(server,pin.get_param("openid.assoc_handle")); 169 assoc_t assoc = retrieve_assoc(server,pin.get_param("openid.assoc_handle"));
169 if(assoc->is_expired()) 170 if(assoc->is_expired())
170 throw id_res_expired_on_delivery(OPKELE_CP_ "retrieve_assoc() has returned expired handle"); 171 throw id_res_expired_on_delivery(OPKELE_CP_ "retrieve_assoc() has returned expired handle");
171 const string& sigenc = pin.get_param("openid.sig"); 172 const string& sigenc = pin.get_param("openid.sig");
172 vector<unsigned char> sig; 173 vector<unsigned char> sig;
173 util::decode_base64(sigenc,sig); 174 util::decode_base64(sigenc,sig);
174 const string& slist = pin.get_param("openid.signed"); 175 const string& slist = pin.get_param("openid.signed");
175 string kv; 176 string kv;
176 string::size_type p = 0; 177 string::size_type p = 0;
177 while(true) { 178 while(true) {
178 string::size_type co = slist.find(',',p); 179 string::size_type co = slist.find(',',p);
179 string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p); 180 string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p);
180 kv += f; 181 kv += f;
181 kv += ':'; 182 kv += ':';
182 f.insert(0,"openid."); 183 f.insert(0,"openid.");
183 kv += pin.get_param(f); 184 kv += pin.get_param(f);
184 kv += '\n'; 185 kv += '\n';
185 if(ext) ps[f.substr(sizeof("openid.")-1)] = pin.get_param(f); 186 if(ext) ps[f.substr(sizeof("openid.")-1)] = pin.get_param(f);
186 if(co==string::npos) 187 if(co==string::npos)
187 break; 188 break;
188 p = co+1; 189 p = co+1;
189 } 190 }
190 secret_t secret = assoc->secret(); 191 secret_t secret = assoc->secret();
191 unsigned int md_len = 0; 192 unsigned int md_len = 0;
192 unsigned char *md = HMAC( 193 unsigned char *md = HMAC(
193 EVP_sha1(), 194 EVP_sha1(),
194 &(secret.front()),secret.size(), 195 &(secret.front()),secret.size(),
195 (const unsigned char *)kv.data(),kv.length(), 196 (const unsigned char *)kv.data(),kv.length(),
196 0,&md_len); 197 0,&md_len);
197 if(sig.size()!=md_len || memcmp(&(sig.front()),md,md_len)) 198 if(sig.size()!=md_len || memcmp(&(sig.front()),md,md_len))
198 throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); 199 throw id_res_mismatch(OPKELE_CP_ "signature mismatch");
199 }catch(failed_lookup& e) { 200 }catch(failed_lookup& e) {
200 const string& slist = pin.get_param("openid.signed"); 201 const string& slist = pin.get_param("openid.signed");
201 string::size_type pp = 0; 202 string::size_type pp = 0;
202 params_t p; 203 params_t p;
203 while(true) { 204 while(true) {
204 string::size_type co = slist.find(',',pp); 205 string::size_type co = slist.find(',',pp);
205 string f = "openid."; 206 string f = "openid.";
206 f += (co==string::npos)?slist.substr(pp):slist.substr(pp,co-pp); 207 f += (co==string::npos)?slist.substr(pp):slist.substr(pp,co-pp);
207 p[f] = pin.get_param(f); 208 p[f] = pin.get_param(f);
208 if(co==string::npos) 209 if(co==string::npos)
209 break; 210 break;
210 pp = co+1; 211 pp = co+1;
211 } 212 }
212 p["openid.assoc_handle"] = pin.get_param("openid.assoc_handle"); 213 p["openid.assoc_handle"] = pin.get_param("openid.assoc_handle");
213 p["openid.sig"] = pin.get_param("openid.sig"); 214 p["openid.sig"] = pin.get_param("openid.sig");
214 p["openid.signed"] = pin.get_param("openid.signed"); 215 p["openid.signed"] = pin.get_param("openid.signed");
215 try { 216 try {
216 string ih = pin.get_param("openid.invalidate_handle"); 217 string ih = pin.get_param("openid.invalidate_handle");
217 p["openid.invalidate_handle"] = ih; 218 p["openid.invalidate_handle"] = ih;
218 }catch(failed_lookup& fl) { } 219 }catch(failed_lookup& fl) { }
219 try { 220 try {
220 check_authentication(server,p); 221 check_authentication(server,p);
221 }catch(failed_check_authentication& fca) { 222 }catch(failed_check_authentication& fca) {
222 throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); 223 throw id_res_failed(OPKELE_CP_ "failed to check_authentication()");
223 } 224 }
224 } 225 }
225 if(ext) ext->id_res_hook(pin,ps); 226 if(ext) ext->id_res_hook(pin,ps);
226 } 227 }
227 228
228 void consumer_t::check_authentication(const string& server,const params_t& p) { 229 void consumer_t::check_authentication(const string& server,const params_t& p) {
229 string request = "openid.mode=check_authentication"; 230 string request = "openid.mode=check_authentication";
230 for(params_t::const_iterator i=p.begin();i!=p.end();++i) { 231 for(params_t::const_iterator i=p.begin();i!=p.end();++i) {
231 if(i->first!="openid.mode") { 232 if(i->first!="openid.mode") {
232 request += '&'; 233 request += '&';
233 request += i->first; 234 request += i->first;
234 request += '='; 235 request += '=';
235 request += util::url_encode(i->second); 236 request += util::url_encode(i->second);
236 } 237 }
237 } 238 }
238 curl_pick_t curl = curl_pick_t::easy_init(); 239 curl_pick_t curl = curl_pick_t::easy_init();
239 if(!curl) 240 if(!curl)
240 throw exception_curl(OPKELE_CP_ "failed to initialize curl"); 241 throw exception_curl(OPKELE_CP_ "failed to initialize curl");
241 CURLcode r; 242 CURLcode r;
242 (r=curl.misc_sets()) 243 (r=curl.misc_sets())
243 || (r=curl.easy_setopt(CURLOPT_URL,server.c_str())) 244 || (r=curl.easy_setopt(CURLOPT_URL,server.c_str()))
244 || (r=curl.easy_setopt(CURLOPT_POST,1)) 245 || (r=curl.easy_setopt(CURLOPT_POST,1))
245 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) 246 || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data()))
246 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) 247 || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length()))
247 || (r=curl.set_write()) 248 || (r=curl.set_write())
248 ; 249 ;
249 if(r) 250 if(r)
250 throw exception_curl(OPKELE_CP_ "failed to set curly options",r); 251 throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
251 if( (r=curl.easy_perform()) ) 252 if( (r=curl.easy_perform()) )
252 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); 253 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
253 params_t pp; pp.parse_keyvalues(curl.response); 254 params_t pp; pp.parse_keyvalues(curl.response);
254 if(pp.has_param("invalidate_handle")) 255 if(pp.has_param("invalidate_handle"))
255 invalidate_assoc(server,pp.get_param("invalidate_handle")); 256 invalidate_assoc(server,pp.get_param("invalidate_handle"));
256 if(pp.has_param("is_valid")) { 257 if(pp.has_param("is_valid")) {
257 if(pp.get_param("is_valid")=="true") 258 if(pp.get_param("is_valid")=="true")
258 return; 259 return;
259 }else if(pp.has_param("lifetime")) { 260 }else if(pp.has_param("lifetime")) {
260 if(util::string_to_long(pp.get_param("lifetime"))) 261 if(util::string_to_long(pp.get_param("lifetime")))
261 return; 262 return;
262 } 263 }
263 throw failed_check_authentication(OPKELE_CP_ "failed to verify response"); 264 throw failed_check_authentication(OPKELE_CP_ "failed to verify response");
264 } 265 }
265 266
266 void consumer_t::retrieve_links(const string& url,string& server,string& delegate) { 267 void consumer_t::retrieve_links(const string& url,string& server,string& delegate) {
267 server.erase(); 268 server.erase();
268 delegate.erase(); 269 delegate.erase();
269 curl_pick_t curl = curl_pick_t::easy_init(); 270 curl_pick_t curl = curl_pick_t::easy_init();
270 if(!curl) 271 if(!curl)
271 throw exception_curl(OPKELE_CP_ "failed to initialize curl"); 272 throw exception_curl(OPKELE_CP_ "failed to initialize curl");
272 string& html = curl.response; 273 string& html = curl.response;
273 CURLcode r; 274 CURLcode r;
274 (r=curl.misc_sets()) 275 (r=curl.misc_sets())
275 || (r=curl.easy_setopt(CURLOPT_URL,url.c_str())) 276 || (r=curl.easy_setopt(CURLOPT_URL,url.c_str()))
276 || (r=curl.set_write()); 277 || (r=curl.set_write());
277 ; 278 ;
278 if(r) 279 if(r)
279 throw exception_curl(OPKELE_CP_ "failed to set curly options",r); 280 throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
280 r = curl.easy_perform(); 281 r = curl.easy_perform();
281 if(r && r!=CURLE_WRITE_ERROR) 282 if(r && r!=CURLE_WRITE_ERROR)
282 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); 283 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
283 static const char *re_bre = "<\\s*body\\b", *re_hdre = "<\\s*head[^>]*>", 284 static const char *re_bre = "<\\s*body\\b", *re_hdre = "<\\s*head[^>]*>",
284 *re_lre = "<\\s*link\\b([^>]+)>", 285 *re_lre = "<\\s*link\\b([^>]+)>",
285 *re_rre = "\\brel\\s*=\\s*['\"]([^'\"]+)['\"]", 286 *re_rre = "\\brel\\s*=\\s*['\"]([^'\"]+)['\"]",
286 *re_hre = "\\bhref\\s*=\\s*['\"]\\s*([^'\"\\s]+)\\s*['\"]"; 287 *re_hre = "\\bhref\\s*=\\s*['\"]\\s*([^'\"\\s]+)\\s*['\"]";
287 pcre_matches_t m1(3), m2(3); 288 pcre_matches_t m1(3), m2(3);
288 pcre_t bre(re_bre,PCRE_CASELESS); 289 pcre_t bre(re_bre,PCRE_CASELESS);
289 if(bre.exec(html,m1)>0) 290 if(bre.exec(html,m1)>0)
290 html.erase(m1.begin(0)); 291 html.erase(m1.begin(0));
291 pcre_t hdre(re_hdre,PCRE_CASELESS); 292 pcre_t hdre(re_hdre,PCRE_CASELESS);
292 if(hdre.exec(html,m1)<=0) 293 if(hdre.exec(html,m1)<=0)
293 throw bad_input(OPKELE_CP_ "failed to find <head>"); 294 throw bad_input(OPKELE_CP_ "failed to find <head>");
294 html.erase(0,m1.end(0)+1); 295 html.erase(0,m1.end(0)+1);
295 pcre_t lre(re_lre,PCRE_CASELESS), rre(re_rre,PCRE_CASELESS), hre(re_hre,PCRE_CASELESS); 296 pcre_t lre(re_lre,PCRE_CASELESS), rre(re_rre,PCRE_CASELESS), hre(re_hre,PCRE_CASELESS);
296 bool gotit = false; 297 bool gotit = false;
297 while( (!gotit) && lre.exec(html,m1)>=2 ) { 298 while( (!gotit) && lre.exec(html,m1)>=2 ) {
298 static const char *whitespace = " \t"; 299 static const char *whitespace = " \t";
299 string attrs(html,m1.begin(1),m1.length(1)); 300 string attrs(html,m1.begin(1),m1.length(1));
300 html.erase(0,m1.end(0)+1); 301 html.erase(0,m1.end(0)+1);
301 if(!( rre.exec(attrs,m1)>=2 && hre.exec(attrs,m2)>=2 )) 302 if(!( rre.exec(attrs,m1)>=2 && hre.exec(attrs,m2)>=2 ))
302 continue; 303 continue;
303 string rels(attrs,m1.begin(1),m1.length(1)); 304 string rels(attrs,m1.begin(1),m1.length(1));
304 for(string::size_type ns = rels.find_first_not_of(whitespace); 305 for(string::size_type ns = rels.find_first_not_of(whitespace);
305 ns!=string::npos; 306 ns!=string::npos;
306 ns=rels.find_first_not_of(whitespace,ns)) { 307 ns=rels.find_first_not_of(whitespace,ns)) {
307 string::size_type s = rels.find_first_of(whitespace,ns); 308 string::size_type s = rels.find_first_of(whitespace,ns);
308 string rel; 309 string rel;
309 if(s==string::npos) { 310 if(s==string::npos) {
310 rel.assign(rels,ns,string::npos); 311 rel.assign(rels,ns,string::npos);
311 ns=string::npos; 312 ns=string::npos;
312 }else{ 313 }else{
313 rel.assign(rels,ns,s-ns); 314 rel.assign(rels,ns,s-ns);
314 ns=s; 315 ns=s;
315 } 316 }
316 if(rel=="openid.server") { 317 if(rel=="openid.server") {
317 server.assign(attrs,m2.begin(1),m2.length(1)); 318 server.assign(attrs,m2.begin(1),m2.length(1));
318 if(!delegate.empty()) { 319 if(!delegate.empty()) {
319 gotit = true; 320 gotit = true;
320 break; 321 break;
321 } 322 }
322 }else if(rel=="openid.delegate") { 323 }else if(rel=="openid.delegate") {
323 delegate.assign(attrs,m2.begin(1),m2.length(1)); 324 delegate.assign(attrs,m2.begin(1),m2.length(1));
324 if(!server.empty()) { 325 if(!server.empty()) {
325 gotit = true; 326 gotit = true;
326 break; 327 break;
327 } 328 }
328 } 329 }
329 } 330 }
330 } 331 }
331 if(server.empty()) 332 if(server.empty())
332 throw failed_assertion(OPKELE_CP_ "The location has no openid.server declaration"); 333 throw failed_assertion(OPKELE_CP_ "The location has no openid.server declaration");
333 } 334 }
334 335
335 assoc_t consumer_t::find_assoc(const string& /* server */) { 336 assoc_t consumer_t::find_assoc(const string& /* server */) {
336 throw failed_lookup(OPKELE_CP_ "no find_assoc() provided"); 337 throw failed_lookup(OPKELE_CP_ "no find_assoc() provided");
337 } 338 }
338 339
339 string consumer_t::normalize(const string& url) { 340 string consumer_t::normalize(const string& url) {
340 string rv = url; 341 string rv = url;
341 // strip leading and trailing spaces 342 // strip leading and trailing spaces
342 string::size_type i = rv.find_first_not_of(" \t\r\n"); 343 string::size_type i = rv.find_first_not_of(" \t\r\n");
343 if(i==string::npos) 344 if(i==string::npos)
344 throw bad_input(OPKELE_CP_ "empty URL"); 345 throw bad_input(OPKELE_CP_ "empty URL");
345 if(i) 346 if(i)
346 rv.erase(0,i); 347 rv.erase(0,i);
347 i = rv.find_last_not_of(" \t\r\n"); 348 i = rv.find_last_not_of(" \t\r\n");
348 assert(i!=string::npos); 349 assert(i!=string::npos);
349 if(i<(rv.length()-1)) 350 if(i<(rv.length()-1))
350 rv.erase(i+1); 351 rv.erase(i+1);
351 // add missing http:// 352 // add missing http://
352 i = rv.find("://"); 353 i = rv.find("://");
353 if(i==string::npos) { // primitive. but do we need more? 354 if(i==string::npos) { // primitive. but do we need more?
354 rv.insert(0,"http://"); 355 rv.insert(0,"http://");
355 i = sizeof("http://")-1; 356 i = sizeof("http://")-1;
356 }else{ 357 }else{
357 i += sizeof("://")-1; 358 i += sizeof("://")-1;
358 } 359 }
359 string::size_type qm = rv.find('?',i); 360 string::size_type qm = rv.find('?',i);
360 string::size_type sl = rv.find('/',i); 361 string::size_type sl = rv.find('/',i);
361 if(qm!=string::npos) { 362 if(qm!=string::npos) {
362 if(sl==string::npos || sl>qm) 363 if(sl==string::npos || sl>qm)
363 rv.insert(qm,1,'/'); 364 rv.insert(qm,1,'/');
364 }else{ 365 }else{
365 if(sl==string::npos) 366 if(sl==string::npos)
366 rv += '/'; 367 rv += '/';
367 } 368 }
368 return rv; 369 return rv;
369 } 370 }
370 371
371 string consumer_t::canonicalize(const string& url) { 372 string consumer_t::canonicalize(const string& url) {
372 string rv = normalize(url); 373 string rv = normalize(url);
373 curl_t curl = curl_t::easy_init(); 374 curl_t curl = curl_t::easy_init();
374 if(!curl) 375 if(!curl)
375 throw exception_curl(OPKELE_CP_ "failed to initialize curl()"); 376 throw exception_curl(OPKELE_CP_ "failed to initialize curl()");
376 string html; 377 string html;
377 CURLcode r; 378 CURLcode r;
378 (r=curl.misc_sets()) 379 (r=curl.misc_sets())
379 || (r=curl.easy_setopt(CURLOPT_URL,rv.c_str())) 380 || (r=curl.easy_setopt(CURLOPT_URL,rv.c_str()))
380 || (r=curl.easy_setopt(CURLOPT_NOBODY,1)) 381 || (r=curl.easy_setopt(CURLOPT_NOBODY,1))
381 ; 382 ;
382 if(r) 383 if(r)
383 throw exception_curl(OPKELE_CP_ "failed to set curly options",r); 384 throw exception_curl(OPKELE_CP_ "failed to set curly options",r);
384 r = curl.easy_perform(); 385 r = curl.easy_perform();
385 if(r) 386 if(r)
386 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); 387 throw exception_curl(OPKELE_CP_ "failed to perform curly request",r);
387 const char *eu = 0; 388 const char *eu = 0;
388 r = curl.easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu); 389 r = curl.easy_getinfo(CURLINFO_EFFECTIVE_URL,&eu);
389 if(r) 390 if(r)
390 throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r); 391 throw exception_curl(OPKELE_CP_ "failed to get CURLINFO_EFFECTIVE_URL",r);
391 rv = eu; 392 rv = eu;
392 return normalize(rv); 393 return normalize(rv);
393 } 394 }
394 395
395} 396}
diff --git a/lib/server.cc b/lib/server.cc
index 776f1ae..0dea1eb 100644
--- a/lib/server.cc
+++ b/lib/server.cc
@@ -1,171 +1,172 @@
1#include <cstring> 1#include <cstring>
2#include <vector> 2#include <vector>
3#include <openssl/sha.h> 3#include <openssl/sha.h>
4#include <openssl/hmac.h> 4#include <openssl/hmac.h>
5#include <opkele/util.h> 5#include <opkele/util.h>
6#include <opkele/util-internal.h>
6#include <opkele/exception.h> 7#include <opkele/exception.h>
7#include <opkele/server.h> 8#include <opkele/server.h>
8#include <opkele/data.h> 9#include <opkele/data.h>
9 10
10namespace opkele { 11namespace opkele {
11 using namespace std; 12 using namespace std;
12 13
13 void server_t::associate(const params_t& pin,params_t& pout) { 14 void server_t::associate(const params_t& pin,params_t& pout) {
14 util::dh_t dh; 15 util::dh_t dh;
15 util::bignum_t c_pub; 16 util::bignum_t c_pub;
16 unsigned char key_sha1[SHA_DIGEST_LENGTH]; 17 unsigned char key_sha1[SHA_DIGEST_LENGTH];
17 enum { 18 enum {
18 sess_cleartext, 19 sess_cleartext,
19 sess_dh_sha1 20 sess_dh_sha1
20 } st = sess_cleartext; 21 } st = sess_cleartext;
21 if( 22 if(
22 pin.has_param("openid.session_type") 23 pin.has_param("openid.session_type")
23 && pin.get_param("openid.session_type")=="DH-SHA1" ) { 24 && pin.get_param("openid.session_type")=="DH-SHA1" ) {
24 /* TODO: fallback to cleartext in case of exceptions here? */ 25 /* TODO: fallback to cleartext in case of exceptions here? */
25 if(!(dh = DH_new())) 26 if(!(dh = DH_new()))
26 throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); 27 throw exception_openssl(OPKELE_CP_ "failed to DH_new()");
27 c_pub = util::base64_to_bignum(pin.get_param("openid.dh_consumer_public")); 28 c_pub = util::base64_to_bignum(pin.get_param("openid.dh_consumer_public"));
28 if(pin.has_param("openid.dh_modulus")) 29 if(pin.has_param("openid.dh_modulus"))
29 dh->p = util::base64_to_bignum(pin.get_param("openid.dh_modulus")); 30 dh->p = util::base64_to_bignum(pin.get_param("openid.dh_modulus"));
30 else 31 else
31 dh->p = util::dec_to_bignum(data::_default_p); 32 dh->p = util::dec_to_bignum(data::_default_p);
32 if(pin.has_param("openid.dh_gen")) 33 if(pin.has_param("openid.dh_gen"))
33 dh->g = util::base64_to_bignum(pin.get_param("openid.dh_gen")); 34 dh->g = util::base64_to_bignum(pin.get_param("openid.dh_gen"));
34 else 35 else
35 dh->g = util::dec_to_bignum(data::_default_g); 36 dh->g = util::dec_to_bignum(data::_default_g);
36 if(!DH_generate_key(dh)) 37 if(!DH_generate_key(dh))
37 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); 38 throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()");
38 vector<unsigned char> ck(DH_size(dh)+1); 39 vector<unsigned char> ck(DH_size(dh)+1);
39 unsigned char *ckptr = &(ck.front())+1; 40 unsigned char *ckptr = &(ck.front())+1;
40 int cklen = DH_compute_key(ckptr,c_pub,dh); 41 int cklen = DH_compute_key(ckptr,c_pub,dh);
41 if(cklen<0) 42 if(cklen<0)
42 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); 43 throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()");
43 if(cklen && (*ckptr)&0x80) { 44 if(cklen && (*ckptr)&0x80) {
44 (*(--ckptr)) = 0; ++cklen; 45 (*(--ckptr)) = 0; ++cklen;
45 } 46 }
46 SHA1(ckptr,cklen,key_sha1); 47 SHA1(ckptr,cklen,key_sha1);
47 st = sess_dh_sha1; 48 st = sess_dh_sha1;
48 } 49 }
49 assoc_t assoc = alloc_assoc(mode_associate); 50 assoc_t assoc = alloc_assoc(mode_associate);
50 time_t now = time(0); 51 time_t now = time(0);
51 pout.clear(); 52 pout.clear();
52 pout["assoc_type"] = assoc->assoc_type(); 53 pout["assoc_type"] = assoc->assoc_type();
53 pout["assoc_handle"] = assoc->handle(); 54 pout["assoc_handle"] = assoc->handle();
54 /* TODO: eventually remove deprecated stuff */ 55 /* TODO: eventually remove deprecated stuff */
55 pout["issued"] = util::time_to_w3c(now); 56 pout["issued"] = util::time_to_w3c(now);
56 pout["expiry"] = util::time_to_w3c(now+assoc->expires_in()); 57 pout["expiry"] = util::time_to_w3c(now+assoc->expires_in());
57 pout["expires_in"] = util::long_to_string(assoc->expires_in()); 58 pout["expires_in"] = util::long_to_string(assoc->expires_in());
58 secret_t secret = assoc->secret(); 59 secret_t secret = assoc->secret();
59 switch(st) { 60 switch(st) {
60 case sess_dh_sha1: 61 case sess_dh_sha1:
61 pout["session_type"] = "DH-SHA1"; 62 pout["session_type"] = "DH-SHA1";
62 pout["dh_server_public"] = util::bignum_to_base64(dh->pub_key); 63 pout["dh_server_public"] = util::bignum_to_base64(dh->pub_key);
63 secret.enxor_to_base64(key_sha1,pout["enc_mac_key"]); 64 secret.enxor_to_base64(key_sha1,pout["enc_mac_key"]);
64 break; 65 break;
65 default: 66 default:
66 secret.to_base64(pout["mac_key"]); 67 secret.to_base64(pout["mac_key"]);
67 break; 68 break;
68 } 69 }
69 } 70 }
70 71
71 void server_t::checkid_immediate(const params_t& pin,string& return_to,params_t& pout,extension_t *ext) { 72 void server_t::checkid_immediate(const params_t& pin,string& return_to,params_t& pout,extension_t *ext) {
72 checkid_(mode_checkid_immediate,pin,return_to,pout,ext); 73 checkid_(mode_checkid_immediate,pin,return_to,pout,ext);
73 } 74 }
74 75
75 void server_t::checkid_setup(const params_t& pin,string& return_to,params_t& pout,extension_t *ext) { 76 void server_t::checkid_setup(const params_t& pin,string& return_to,params_t& pout,extension_t *ext) {
76 checkid_(mode_checkid_setup,pin,return_to,pout,ext); 77 checkid_(mode_checkid_setup,pin,return_to,pout,ext);
77 } 78 }
78 79
79 void server_t::checkid_(mode_t mode,const params_t& pin,string& return_to,params_t& pout,extension_t *ext) { 80 void server_t::checkid_(mode_t mode,const params_t& pin,string& return_to,params_t& pout,extension_t *ext) {
80 if(mode!=mode_checkid_immediate && mode!=mode_checkid_setup) 81 if(mode!=mode_checkid_immediate && mode!=mode_checkid_setup)
81 throw bad_input(OPKELE_CP_ "invalid checkid_* mode"); 82 throw bad_input(OPKELE_CP_ "invalid checkid_* mode");
82 pout.clear(); 83 pout.clear();
83 assoc_t assoc; 84 assoc_t assoc;
84 try { 85 try {
85 assoc = retrieve_assoc(pin.get_param("openid.assoc_handle")); 86 assoc = retrieve_assoc(pin.get_param("openid.assoc_handle"));
86 }catch(failed_lookup& fl) { 87 }catch(failed_lookup& fl) {
87 // no handle specified or no valid handle found, going dumb 88 // no handle specified or no valid handle found, going dumb
88 assoc = alloc_assoc(mode_checkid_setup); 89 assoc = alloc_assoc(mode_checkid_setup);
89 if(pin.has_param("openid.assoc_handle")) 90 if(pin.has_param("openid.assoc_handle"))
90 pout["invalidate_handle"]=pin.get_param("openid.assoc_handle"); 91 pout["invalidate_handle"]=pin.get_param("openid.assoc_handle");
91 } 92 }
92 string trust_root; 93 string trust_root;
93 try { 94 try {
94 trust_root = pin.get_param("openid.trust_root"); 95 trust_root = pin.get_param("openid.trust_root");
95 }catch(failed_lookup& fl) { } 96 }catch(failed_lookup& fl) { }
96 string identity = pin.get_param("openid.identity"); 97 string identity = pin.get_param("openid.identity");
97 return_to = pin.get_param("openid.return_to"); 98 return_to = pin.get_param("openid.return_to");
98 validate(*assoc,pin,identity,trust_root); 99 validate(*assoc,pin,identity,trust_root);
99 pout["mode"] = "id_res"; 100 pout["mode"] = "id_res";
100 pout["assoc_handle"] = assoc->handle(); 101 pout["assoc_handle"] = assoc->handle();
101 if(pin.has_param("openid.assoc_handle") && assoc->stateless()) 102 if(pin.has_param("openid.assoc_handle") && assoc->stateless())
102 pout["invalidate_handle"] = pin.get_param("openid.assoc_handle"); 103 pout["invalidate_handle"] = pin.get_param("openid.assoc_handle");
103 pout["identity"] = identity; 104 pout["identity"] = identity;
104 pout["return_to"] = return_to; 105 pout["return_to"] = return_to;
105 /* TODO: eventually remove deprecated stuff */ 106 /* TODO: eventually remove deprecated stuff */
106 time_t now = time(0); 107 time_t now = time(0);
107 pout["issued"] = util::time_to_w3c(now); 108 pout["issued"] = util::time_to_w3c(now);
108 pout["valid_to"] = util::time_to_w3c(now+120); 109 pout["valid_to"] = util::time_to_w3c(now+120);
109 pout["exipres_in"] = "120"; 110 pout["exipres_in"] = "120";
110 pout["signed"]="mode,identity,return_to"; 111 pout["signed"]="mode,identity,return_to";
111 if(ext) ext->checkid_hook(pin,pout); 112 if(ext) ext->checkid_hook(pin,pout);
112 pout["sig"] = util::base64_signature(assoc,pout); 113 pout["sig"] = util::base64_signature(assoc,pout);
113 } 114 }
114 115
115 void server_t::check_authentication(const params_t& pin,params_t& pout) { 116 void server_t::check_authentication(const params_t& pin,params_t& pout) {
116 vector<unsigned char> sig; 117 vector<unsigned char> sig;
117 const string& sigenc = pin.get_param("openid.sig"); 118 const string& sigenc = pin.get_param("openid.sig");
118 util::decode_base64(sigenc,sig); 119 util::decode_base64(sigenc,sig);
119 assoc_t assoc; 120 assoc_t assoc;
120 try { 121 try {
121 assoc = retrieve_assoc(pin.get_param("openid.assoc_handle")); 122 assoc = retrieve_assoc(pin.get_param("openid.assoc_handle"));
122 }catch(failed_lookup& fl) { 123 }catch(failed_lookup& fl) {
123 throw failed_assertion(OPKELE_CP_ "invalid handle or handle not specified"); 124 throw failed_assertion(OPKELE_CP_ "invalid handle or handle not specified");
124 } 125 }
125 if(!assoc->stateless()) 126 if(!assoc->stateless())
126 throw stateful_handle(OPKELE_CP_ "will not do check_authentication on a stateful handle"); 127 throw stateful_handle(OPKELE_CP_ "will not do check_authentication on a stateful handle");
127 const string& slist = pin.get_param("openid.signed"); 128 const string& slist = pin.get_param("openid.signed");
128 string kv; 129 string kv;
129 string::size_type p =0; 130 string::size_type p =0;
130 while(true) { 131 while(true) {
131 string::size_type co = slist.find(',',p); 132 string::size_type co = slist.find(',',p);
132 string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p); 133 string f = (co==string::npos)?slist.substr(p):slist.substr(p,co-p);
133 kv += f; 134 kv += f;
134 kv += ':'; 135 kv += ':';
135 if(f=="mode") 136 if(f=="mode")
136 kv += "id_res"; 137 kv += "id_res";
137 else { 138 else {
138 f.insert(0,"openid."); 139 f.insert(0,"openid.");
139 kv += pin.get_param(f); 140 kv += pin.get_param(f);
140 } 141 }
141 kv += '\n'; 142 kv += '\n';
142 if(co==string::npos) 143 if(co==string::npos)
143 break; 144 break;
144 p = co+1; 145 p = co+1;
145 } 146 }
146 secret_t secret = assoc->secret(); 147 secret_t secret = assoc->secret();
147 unsigned int md_len = 0; 148 unsigned int md_len = 0;
148 unsigned char *md = HMAC( 149 unsigned char *md = HMAC(
149 EVP_sha1(), 150 EVP_sha1(),
150 &(secret.front()),secret.size(), 151 &(secret.front()),secret.size(),
151 (const unsigned char *)kv.data(),kv.length(), 152 (const unsigned char *)kv.data(),kv.length(),
152 0,&md_len); 153 0,&md_len);
153 pout.clear(); 154 pout.clear();
154 if(sig.size()==md_len && !memcmp(&(sig.front()),md,md_len)) { 155 if(sig.size()==md_len && !memcmp(&(sig.front()),md,md_len)) {
155 pout["is_valid"]="true"; 156 pout["is_valid"]="true";
156 pout["lifetime"]="60"; /* TODO: eventually remove deprecated stuff */ 157 pout["lifetime"]="60"; /* TODO: eventually remove deprecated stuff */
157 }else{ 158 }else{
158 pout["is_valid"]="false"; 159 pout["is_valid"]="false";
159 pout["lifetime"]="0"; /* TODO: eventually remove deprecated stuff */ 160 pout["lifetime"]="0"; /* TODO: eventually remove deprecated stuff */
160 } 161 }
161 if(pin.has_param("openid.invalidate_handle")) { 162 if(pin.has_param("openid.invalidate_handle")) {
162 string h = pin.get_param("openid.invalidate_handle"); 163 string h = pin.get_param("openid.invalidate_handle");
163 try { 164 try {
164 assoc_t tmp = retrieve_assoc(h); 165 assoc_t tmp = retrieve_assoc(h);
165 }catch(invalid_handle& ih) { 166 }catch(invalid_handle& ih) {
166 pout["invalidate_handle"] = h; 167 pout["invalidate_handle"] = h;
167 }catch(failed_lookup& fl) { } 168 }catch(failed_lookup& fl) { }
168 } 169 }
169 } 170 }
170 171
171} 172}
diff --git a/test/idiscover.cc b/test/idiscover.cc
index 44df9ce..4b1e90c 100644
--- a/test/idiscover.cc
+++ b/test/idiscover.cc
@@ -1,40 +1,41 @@
1#include <iostream> 1#include <iostream>
2#include <stdexcept> 2#include <stdexcept>
3#include <iterator> 3#include <iterator>
4#include <algorithm> 4#include <algorithm>
5using namespace std; 5using namespace std;
6#include <opkele/exception.h> 6#include <opkele/exception.h>
7#include <opkele/discovery.h> 7#include <opkele/discovery.h>
8#include <opkele/util.h> 8#include <opkele/util.h>
9#include <opkele/util-internal.h>
9 10
10namespace opkele { 11namespace opkele {
11 ostream& operator<<(ostream& o,const opkele::openid_endpoint_t& oep) { 12 ostream& operator<<(ostream& o,const opkele::openid_endpoint_t& oep) {
12 o 13 o
13 << " URI: " << oep.uri << endl 14 << " URI: " << oep.uri << endl
14 << " Claimed ID: " << oep.claimed_id << endl 15 << " Claimed ID: " << oep.claimed_id << endl
15 << " Local ID: " << oep.local_id << endl; 16 << " Local ID: " << oep.local_id << endl;
16 return o; 17 return o;
17 } 18 }
18} 19}
19 20
20int main(int argc,char **argv) { 21int main(int argc,char **argv) {
21 try { 22 try {
22 if(argc<2) 23 if(argc<2)
23 throw opkele::exception(OPKELE_CP_ "Please, give me something to resolve"); 24 throw opkele::exception(OPKELE_CP_ "Please, give me something to resolve");
24 for(int a=1;a<argc;++a) { 25 for(int a=1;a<argc;++a) {
25 cout << "==============================================================" << endl 26 cout << "==============================================================" << endl
26 << "User-supplied ID: " << argv[a] << endl 27 << "User-supplied ID: " << argv[a] << endl
27 << "Endpoints:" << endl 28 << "Endpoints:" << endl
28 << " --" << endl; 29 << " --" << endl;
29 string normalized = opkele::idiscover( 30 string normalized = opkele::idiscover(
30 ostream_iterator<opkele::openid_endpoint_t>(cout," --\n") 31 ostream_iterator<opkele::openid_endpoint_t>(cout," --\n")
31 ,argv[a]); 32 ,argv[a]);
32 cout << "Normalized ID: " << normalized << endl; 33 cout << "Normalized ID: " << normalized << endl;
33 } 34 }
34 }catch(exception& e) { 35 }catch(exception& e) {
35 cerr << "oops, caught " << opkele::util::abi_demangle(typeid(e).name()) << endl 36 cerr << "oops, caught " << opkele::util::abi_demangle(typeid(e).name()) << endl
36 << " .what(): " << e.what() << endl; 37 << " .what(): " << e.what() << endl;
37 _exit(1); 38 _exit(1);
38 } 39 }
39 _exit(0); 40 _exit(0);
40} 41}