-rw-r--r-- | include/Makefile.am | 3 | ||||
-rw-r--r-- | include/opkele/util-internal.h | 92 | ||||
-rw-r--r-- | include/opkele/util.h | 92 | ||||
-rw-r--r-- | lib/basic_op.cc | 1 | ||||
-rw-r--r-- | lib/basic_rp.cc | 1 | ||||
-rw-r--r-- | lib/consumer.cc | 1 | ||||
-rw-r--r-- | lib/server.cc | 1 | ||||
-rw-r--r-- | test/idiscover.cc | 1 |
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 @@ | |||
1 | NODIST_HEADERS_ = \ | 1 | NODIST_HEADERS_ = \ |
2 | opkele/acconfig.h \ | 2 | opkele/acconfig.h \ |
3 | opkele/tr1-mem.h | 3 | opkele/tr1-mem.h |
4 | 4 | ||
5 | nobase_include_HEADERS = \ | 5 | nobase_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 | ||
24 | noinst_HEADERS = \ | 25 | noinst_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 | ||
31 | dist-hook: | 32 | dist-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 | |||
7 | namespace 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 | ||
11 | namespace opkele { | 9 | namespace 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 | ||
11 | namespace opkele { | 12 | namespace 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 | ||
11 | namespace opkele { | 12 | namespace 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 | ||
17 | namespace opkele { | 18 | namespace 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 | ||
10 | namespace opkele { | 11 | namespace 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> |
5 | using namespace std; | 5 | using 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 | ||
10 | namespace opkele { | 11 | namespace 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 | ||
20 | int main(int argc,char **argv) { | 21 | int 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 | } |