author | Michael Krelin <hacker@klever.net> | 2008-02-04 22:39:59 (UTC) |
---|---|---|
committer | Michael Krelin <hacker@klever.net> | 2008-02-04 22:39:59 (UTC) |
commit | 9163a26ec8839a31df888920418280a62ebc5595 (patch) (unidiff) | |
tree | 55339b4ecf0a3f24817eb5cc1b0b24f831ac895b | |
parent | c0eeee1cfd41d0f5f6ff6ac3d6fe021421376a69 (diff) | |
download | libopkele-9163a26ec8839a31df888920418280a62ebc5595.zip libopkele-9163a26ec8839a31df888920418280a62ebc5595.tar.gz libopkele-9163a26ec8839a31df888920418280a62ebc5595.tar.bz2 |
reworked extensions framework
* changed {checkid,id_res}_hook to {rp,op}_{checkid,id_res}_hook
* deprecated older hooks, although implemented it in sreg and chain extensions
* added extension processing to basic_op
* added sreg to test OP
Signed-off-by: Michael Krelin <hacker@klever.net>
-rw-r--r-- | include/opkele/basic_op.h | 3 | ||||
-rw-r--r-- | include/opkele/extension.h | 49 | ||||
-rw-r--r-- | include/opkele/extension_chain.h | 6 | ||||
-rw-r--r-- | include/opkele/sreg.h | 31 | ||||
-rw-r--r-- | lib/basic_op.cc | 5 | ||||
-rw-r--r-- | lib/basic_rp.cc | 4 | ||||
-rw-r--r-- | lib/extension.cc | 25 | ||||
-rw-r--r-- | lib/extension_chain.cc | 27 | ||||
-rw-r--r-- | lib/sreg.cc | 28 | ||||
-rw-r--r-- | test/OP.cc | 10 |
10 files changed, 121 insertions, 67 deletions
diff --git a/include/opkele/basic_op.h b/include/opkele/basic_op.h index 7f4e481..5bba1bf 100644 --- a/include/opkele/basic_op.h +++ b/include/opkele/basic_op.h | |||
@@ -1,68 +1,69 @@ | |||
1 | #ifndef __OPKELE_BASIC_OP_H | 1 | #ifndef __OPKELE_BASIC_OP_H |
2 | #define __OPKELE_BASIC_OP_H | 2 | #define __OPKELE_BASIC_OP_H |
3 | 3 | ||
4 | #include <string> | 4 | #include <string> |
5 | #include <opkele/types.h> | 5 | #include <opkele/types.h> |
6 | #include <opkele/extension.h> | 6 | #include <opkele/extension.h> |
7 | 7 | ||
8 | namespace opkele { | 8 | namespace opkele { |
9 | using std::string; | 9 | using std::string; |
10 | 10 | ||
11 | class basic_op { | 11 | class basic_op { |
12 | public: | 12 | public: |
13 | mode_t mode; | 13 | mode_t mode; |
14 | assoc_t assoc; | 14 | assoc_t assoc; |
15 | bool openid2; | 15 | bool openid2; |
16 | string return_to; | 16 | string return_to; |
17 | string realm; | 17 | string realm; |
18 | string claimed_id; | 18 | string claimed_id; |
19 | string identity; | 19 | string identity; |
20 | string invalidate_handle; | 20 | string invalidate_handle; |
21 | 21 | ||
22 | void reset_vars(); | 22 | void reset_vars(); |
23 | 23 | ||
24 | bool has_return_to() const; | 24 | bool has_return_to() const; |
25 | const string& get_return_to() const; | 25 | const string& get_return_to() const; |
26 | 26 | ||
27 | const string& get_realm() const; | 27 | const string& get_realm() const; |
28 | 28 | ||
29 | bool has_identity() const; | 29 | bool has_identity() const; |
30 | const string& get_claimed_id() const; | 30 | const string& get_claimed_id() const; |
31 | const string& get_identity() const; | 31 | const string& get_identity() const; |
32 | 32 | ||
33 | bool is_id_select() const; | 33 | bool is_id_select() const; |
34 | 34 | ||
35 | void select_identity(const string& c,const string& i); | 35 | void select_identity(const string& c,const string& i); |
36 | void set_claimed_id(const string& c); | 36 | void set_claimed_id(const string& c); |
37 | 37 | ||
38 | basic_openid_message& associate( | 38 | basic_openid_message& associate( |
39 | basic_openid_message& oum, | 39 | basic_openid_message& oum, |
40 | const basic_openid_message& inm); | 40 | const basic_openid_message& inm); |
41 | 41 | ||
42 | void checkid_(const basic_openid_message& inm,extension_t *ext=0); | 42 | void checkid_(const basic_openid_message& inm,extension_t *ext=0); |
43 | basic_openid_message& id_res(basic_openid_message& om); | 43 | basic_openid_message& id_res(basic_openid_message& om, |
44 | extension_t *ext=0); | ||
44 | basic_openid_message& cancel(basic_openid_message& om); | 45 | basic_openid_message& cancel(basic_openid_message& om); |
45 | basic_openid_message& error(basic_openid_message& om, | 46 | basic_openid_message& error(basic_openid_message& om, |
46 | const string& error,const string& contact, | 47 | const string& error,const string& contact, |
47 | const string& reference ); | 48 | const string& reference ); |
48 | basic_openid_message& setup_needed( | 49 | basic_openid_message& setup_needed( |
49 | basic_openid_message& oum,const basic_openid_message& inm); | 50 | basic_openid_message& oum,const basic_openid_message& inm); |
50 | 51 | ||
51 | basic_openid_message& check_authentication( | 52 | basic_openid_message& check_authentication( |
52 | basic_openid_message& oum,const basic_openid_message& inm); | 53 | basic_openid_message& oum,const basic_openid_message& inm); |
53 | 54 | ||
54 | virtual void verify_return_to(); | 55 | virtual void verify_return_to(); |
55 | 56 | ||
56 | virtual assoc_t alloc_assoc(const string& t,size_t kl,bool sl) = 0; | 57 | virtual assoc_t alloc_assoc(const string& t,size_t kl,bool sl) = 0; |
57 | virtual assoc_t retrieve_assoc(const string& h) = 0; | 58 | virtual assoc_t retrieve_assoc(const string& h) = 0; |
58 | 59 | ||
59 | virtual string& alloc_nonce(string& nonce,bool sl) = 0; | 60 | virtual string& alloc_nonce(string& nonce,bool sl) = 0; |
60 | virtual bool check_nonce(const string& nonce) = 0; | 61 | virtual bool check_nonce(const string& nonce) = 0; |
61 | virtual void invalidate_nonce(const string& nonce) = 0; | 62 | virtual void invalidate_nonce(const string& nonce) = 0; |
62 | 63 | ||
63 | virtual const string get_op_endpoint() const = 0; | 64 | virtual const string get_op_endpoint() const = 0; |
64 | 65 | ||
65 | }; | 66 | }; |
66 | } | 67 | } |
67 | 68 | ||
68 | #endif /* __OPKELE_BASIC_OP_H */ | 69 | #endif /* __OPKELE_BASIC_OP_H */ |
diff --git a/include/opkele/extension.h b/include/opkele/extension.h index 3ee25ee..37bcb90 100644 --- a/include/opkele/extension.h +++ b/include/opkele/extension.h | |||
@@ -1,66 +1,61 @@ | |||
1 | #ifndef __OPKELE_EXTENSION_H | 1 | #ifndef __OPKELE_EXTENSION_H |
2 | #define __OPKELE_EXTENSION_H | 2 | #define __OPKELE_EXTENSION_H |
3 | 3 | ||
4 | /** | 4 | /** |
5 | * @file | 5 | * @file |
6 | * @brief extensions framework basics | 6 | * @brief extensions framework basics |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <opkele/opkele-config.h> | ||
9 | #include <opkele/types.h> | 10 | #include <opkele/types.h> |
10 | 11 | ||
11 | namespace opkele { | 12 | namespace opkele { |
12 | 13 | ||
13 | /** | 14 | /** |
14 | * OpenID extension hooks base class | 15 | * OpenID extension hooks base class |
15 | */ | 16 | */ |
16 | class extension_t { | 17 | class extension_t { |
17 | public: | 18 | public: |
18 | 19 | ||
19 | virtual ~extension_t() { } | 20 | virtual ~extension_t() { } |
20 | 21 | ||
21 | /** | 22 | /** |
22 | * hook called by consumer before submitting data to OpenID server. | 23 | * hook called by RP before submitting the message to OP. |
23 | * It is supposed to manipulate parameters list. | 24 | * @param om openid message to be submit |
24 | * @param p parameters about to be submitted to server | ||
25 | * @param identity identity being verified. It may differ from the | ||
26 | * one available in parameters list in case of delegation | ||
27 | * @see consumer_t::checkid_ | ||
28 | * @see consumer_t::checkid_immediate | ||
29 | * @see consumer_t::checkid_setup | ||
30 | */ | 25 | */ |
31 | virtual void checkid_hook(basic_openid_message& om); | 26 | virtual void rp_checkid_hook(basic_openid_message& om); |
27 | |||
32 | /** | 28 | /** |
33 | * hook called by consumer after identity information received from | 29 | * hook called by RP after verifying information received from OP. |
34 | * OpenID server is verified. | 30 | * @param om openid message received |
35 | * @param p parameters received from server | 31 | * @param sp signed part of the message |
36 | * @param sp signed parameters received from server with 'openid.' | ||
37 | * leader stripped | ||
38 | * @param identity identity confirmed. May differ from the one | ||
39 | * available in parameters list in case of delegation. May also be | ||
40 | * empty which means - extract one from parameters | ||
41 | * @see consumer_t::id_res | ||
42 | */ | 32 | */ |
43 | virtual void id_res_hook(const basic_openid_message& om,const basic_openid_message& sp); | 33 | virtual void rp_id_res_hook(const basic_openid_message& om, |
34 | const basic_openid_message& sp); | ||
44 | 35 | ||
45 | /** | 36 | /** |
46 | * hook called by server before returning information to consumer. | 37 | * hook called by OP after parsing incoming message |
47 | * The hook may manipulate output parameters. It is important to | 38 | * @param inm message received from RP |
48 | * note that modified pout["signed"] is used for signing response. | 39 | */ |
49 | * @param pin request parameters list with "openid." prefix | 40 | virtual void op_checkid_hook(const basic_openid_message& inm); |
50 | * @param pout response parameters list without "openid." prefix | 41 | /** |
51 | * @see server_t::checkid_ | 42 | * hook called by OP before signing the reply to RP |
52 | * @see server_t::checkid_immediate | 43 | * @param oum message to be sent to RP |
53 | * @see server_t::checkid_setup | ||
54 | */ | 44 | */ |
45 | virtual void op_id_res_hook(basic_openid_message& oum); | ||
46 | |||
47 | virtual void checkid_hook(basic_openid_message& om) OPKELE_DEPRECATE; | ||
48 | virtual void id_res_hook(const basic_openid_message& om, | ||
49 | const basic_openid_message& sp) OPKELE_DEPRECATE; | ||
55 | virtual void checkid_hook(const basic_openid_message& inm,basic_openid_message& oum); | 50 | virtual void checkid_hook(const basic_openid_message& inm,basic_openid_message& oum); |
56 | 51 | ||
57 | /** | 52 | /** |
58 | * Casts the object to pointer to itself. For convenient passing | 53 | * Casts the object to pointer to itself. For convenient passing |
59 | * of pointer. | 54 | * of pointer. |
60 | */ | 55 | */ |
61 | operator extension_t*(void) { return this; } | 56 | operator extension_t*(void) { return this; } |
62 | }; | 57 | }; |
63 | 58 | ||
64 | } | 59 | } |
65 | 60 | ||
66 | #endif /* __OPKELE_EXTENSION_H */ | 61 | #endif /* __OPKELE_EXTENSION_H */ |
diff --git a/include/opkele/extension_chain.h b/include/opkele/extension_chain.h index fb9bc84..9692934 100644 --- a/include/opkele/extension_chain.h +++ b/include/opkele/extension_chain.h | |||
@@ -1,38 +1,44 @@ | |||
1 | #ifndef __OPKELE_EXTENSION_CHAIN_H | 1 | #ifndef __OPKELE_EXTENSION_CHAIN_H |
2 | #define __OPKELE_EXTENSION_CHAIN_H | 2 | #define __OPKELE_EXTENSION_CHAIN_H |
3 | 3 | ||
4 | /** | 4 | /** |
5 | * @file | 5 | * @file |
6 | * @brief extension chain extension | 6 | * @brief extension chain extension |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <list> | 9 | #include <list> |
10 | #include <opkele/extension.h> | 10 | #include <opkele/extension.h> |
11 | 11 | ||
12 | namespace opkele { | 12 | namespace opkele { |
13 | using std::list; | 13 | using std::list; |
14 | 14 | ||
15 | /** | 15 | /** |
16 | * OpenID extensions chain used to combine extensions, it is actually an | 16 | * OpenID extensions chain used to combine extensions, it is actually an |
17 | * stl list of pointers to extensions. | 17 | * stl list of pointers to extensions. |
18 | */ | 18 | */ |
19 | class extension_chain_t : public extension_t, public list<extension_t*> { | 19 | class extension_chain_t : public extension_t, public list<extension_t*> { |
20 | public: | 20 | public: |
21 | 21 | ||
22 | /** | 22 | /** |
23 | * Default constructor creates an empty chain | 23 | * Default constructor creates an empty chain |
24 | */ | 24 | */ |
25 | extension_chain_t() { } | 25 | extension_chain_t() { } |
26 | /** | 26 | /** |
27 | * Create extension chain with a single extension in it | 27 | * Create extension chain with a single extension in it |
28 | */ | 28 | */ |
29 | extension_chain_t(extension_t *e) { push_back(e); } | 29 | extension_chain_t(extension_t *e) { push_back(e); } |
30 | 30 | ||
31 | virtual void rp_checkid_hook(basic_openid_message& om); | ||
32 | virtual void rp_id_res_hook(const basic_openid_message& om, | ||
33 | const basic_openid_message& sp); | ||
34 | virtual void op_checkid_hook(const basic_openid_message& inm); | ||
35 | virtual void op_id_res_hook(basic_openid_message& oum); | ||
36 | |||
31 | virtual void checkid_hook(basic_openid_message& om); | 37 | virtual void checkid_hook(basic_openid_message& om); |
32 | virtual void id_res_hook(const basic_openid_message& om,const basic_openid_message& sp); | 38 | virtual void id_res_hook(const basic_openid_message& om,const basic_openid_message& sp); |
33 | virtual void checkid_hook(const basic_openid_message& inm,basic_openid_message& oum); | 39 | virtual void checkid_hook(const basic_openid_message& inm,basic_openid_message& oum); |
34 | }; | 40 | }; |
35 | 41 | ||
36 | } | 42 | } |
37 | 43 | ||
38 | #endif /* __OPKELE_EXTENSION_CHAIN_H */ | 44 | #endif /* __OPKELE_EXTENSION_CHAIN_H */ |
diff --git a/include/opkele/sreg.h b/include/opkele/sreg.h index 24cb315..513e221 100644 --- a/include/opkele/sreg.h +++ b/include/opkele/sreg.h | |||
@@ -1,203 +1,204 @@ | |||
1 | #ifndef __OPKELE_SREG_H | 1 | #ifndef __OPKELE_SREG_H |
2 | #define __OPKELE_SREG_H | 2 | #define __OPKELE_SREG_H |
3 | 3 | ||
4 | /** | 4 | /** |
5 | * @file | 5 | * @file |
6 | * @brief Simple registration extension | 6 | * @brief Simple registration extension |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <opkele/extension.h> | 9 | #include <opkele/extension.h> |
10 | 10 | ||
11 | namespace opkele { | 11 | namespace opkele { |
12 | using std::map; | 12 | using std::map; |
13 | 13 | ||
14 | /** | 14 | /** |
15 | * OpenID simple registration extension implementation | 15 | * OpenID simple registration extension implementation |
16 | * http://openid.net/specs/openid-simple-registration-extension-1_0.html | 16 | * http://openid.net/specs/openid-simple-registration-extension-1_0.html |
17 | */ | 17 | */ |
18 | class sreg_t : public extension_t { | 18 | class sreg_t : public extension_t { |
19 | public: | 19 | public: |
20 | /** | 20 | /** |
21 | * sreg fields enumeration | 21 | * sreg fields enumeration |
22 | */ | 22 | */ |
23 | enum fieldbit_t { | 23 | enum fieldbit_t { |
24 | /** | 24 | /** |
25 | * Any UTF-8 string that the End User wants to use as a nickname. | 25 | * Any UTF-8 string that the End User wants to use as a nickname. |
26 | */ | 26 | */ |
27 | field_nickname = 1, | 27 | field_nickname = 1, |
28 | /** | 28 | /** |
29 | * The email address of the End User as specified in section 3.4.1 of [RFC2822] | 29 | * The email address of the End User as specified in section 3.4.1 of [RFC2822] |
30 | */ | 30 | */ |
31 | field_email = 2, | 31 | field_email = 2, |
32 | /** | 32 | /** |
33 | * UTF-8 string free text representation of the End User's full name. | 33 | * UTF-8 string free text representation of the End User's full name. |
34 | */ | 34 | */ |
35 | field_fullname = 4, | 35 | field_fullname = 4, |
36 | /** | 36 | /** |
37 | * The End User's date of birth as YYYY-MM-DD. Any values whose | 37 | * The End User's date of birth as YYYY-MM-DD. Any values whose |
38 | * representation uses fewer than the specified number of | 38 | * representation uses fewer than the specified number of |
39 | * digits should be zero-padded. The length of this value MUST | 39 | * digits should be zero-padded. The length of this value MUST |
40 | * always be 10. If the End User user does not want to reveal | 40 | * always be 10. If the End User user does not want to reveal |
41 | * any particular component of this value, it MUST be set to | 41 | * any particular component of this value, it MUST be set to |
42 | * zero. | 42 | * zero. |
43 | * | 43 | * |
44 | * For instance, if a End User wants to specify that his date | 44 | * For instance, if a End User wants to specify that his date |
45 | * of birth is in 1980, but not the month or day, the value | 45 | * of birth is in 1980, but not the month or day, the value |
46 | * returned SHALL be "1980-00-00". | 46 | * returned SHALL be "1980-00-00". |
47 | */ | 47 | */ |
48 | field_dob = 8, | 48 | field_dob = 8, |
49 | /** | 49 | /** |
50 | * Alias to field_dob | 50 | * Alias to field_dob |
51 | */ | 51 | */ |
52 | field_birthdate = field_dob, | 52 | field_birthdate = field_dob, |
53 | /** | 53 | /** |
54 | * The End User's gender, "M" for male, "F" for female. | 54 | * The End User's gender, "M" for male, "F" for female. |
55 | */ | 55 | */ |
56 | field_gender = 16, | 56 | field_gender = 16, |
57 | /** | 57 | /** |
58 | * Alias to field_gender | 58 | * Alias to field_gender |
59 | */ | 59 | */ |
60 | field_sex = field_gender, | 60 | field_sex = field_gender, |
61 | /** | 61 | /** |
62 | * UTF-8 string free text that SHOULD conform to the End User's | 62 | * UTF-8 string free text that SHOULD conform to the End User's |
63 | * country's postal system. | 63 | * country's postal system. |
64 | */ | 64 | */ |
65 | field_postcode = 32, | 65 | field_postcode = 32, |
66 | /** | 66 | /** |
67 | * The End User's country of residence as specified by ISO3166 | 67 | * The End User's country of residence as specified by ISO3166 |
68 | */ | 68 | */ |
69 | field_country = 64, | 69 | field_country = 64, |
70 | /** | 70 | /** |
71 | * End User's preferred language as specified by ISO639 | 71 | * End User's preferred language as specified by ISO639 |
72 | */ | 72 | */ |
73 | field_language = 128, | 73 | field_language = 128, |
74 | /** | 74 | /** |
75 | * ASCII string from TimeZone database | 75 | * ASCII string from TimeZone database |
76 | * | 76 | * |
77 | * For example, "Europe/Paris" or "America/Los_Angeles". | 77 | * For example, "Europe/Paris" or "America/Los_Angeles". |
78 | */ | 78 | */ |
79 | field_timezone = 256, | 79 | field_timezone = 256, |
80 | /** | 80 | /** |
81 | * All fields bits combined | 81 | * All fields bits combined |
82 | */ | 82 | */ |
83 | fields_ALL = 511, | 83 | fields_ALL = 511, |
84 | /** | 84 | /** |
85 | * No fields | 85 | * No fields |
86 | */ | 86 | */ |
87 | fields_NONE = 0 | 87 | fields_NONE = 0 |
88 | }; | 88 | }; |
89 | /** | 89 | /** |
90 | * Bitmask for fields which, if absent from the response, will | 90 | * Bitmask for fields which, if absent from the response, will |
91 | * prevent the Consumer from completing the registration without | 91 | * prevent the Consumer from completing the registration without |
92 | * End User interation. | 92 | * End User interation. |
93 | */ | 93 | */ |
94 | long fields_required; | 94 | long fields_required; |
95 | /** | 95 | /** |
96 | * Bitmask for fields that will be used by the Consumer, but whose | 96 | * Bitmask for fields that will be used by the Consumer, but whose |
97 | * absence will not prevent the registration from completing. | 97 | * absence will not prevent the registration from completing. |
98 | */ | 98 | */ |
99 | long fields_optional; | 99 | long fields_optional; |
100 | /** | 100 | /** |
101 | * A URL which the Consumer provides to give the End User a place | 101 | * A URL which the Consumer provides to give the End User a place |
102 | * to read about the how the profile data will be used. The | 102 | * to read about the how the profile data will be used. The |
103 | * Identity Provider SHOULD display this URL to the End User if it | 103 | * Identity Provider SHOULD display this URL to the End User if it |
104 | * is given. | 104 | * is given. |
105 | */ | 105 | */ |
106 | string policy_url; | 106 | string policy_url; |
107 | 107 | ||
108 | /** | 108 | /** |
109 | * Bitmask for fields present in response | 109 | * Bitmask for fields present in response |
110 | */ | 110 | */ |
111 | long has_fields; | 111 | long has_fields; |
112 | /** | 112 | /** |
113 | * Container type for response fields values | 113 | * Container type for response fields values |
114 | */ | 114 | */ |
115 | typedef map<fieldbit_t,string> response_t; | 115 | typedef map<fieldbit_t,string> response_t; |
116 | /** | 116 | /** |
117 | * Response contents | 117 | * Response contents |
118 | */ | 118 | */ |
119 | response_t response; | 119 | response_t response; |
120 | 120 | ||
121 | /** | 121 | /** |
122 | * Fields bitmask to send in response | 122 | * Fields bitmask to send in response |
123 | */ | 123 | */ |
124 | long fields_response; | 124 | long fields_response; |
125 | 125 | ||
126 | /** | 126 | /** |
127 | * Consumer constructor. | 127 | * Consumer constructor. |
128 | * @param fr required fields | 128 | * @param fr required fields |
129 | * @see fields_required | 129 | * @see fields_required |
130 | * @param fo optional fields | 130 | * @param fo optional fields |
131 | * @see fields_optional | 131 | * @see fields_optional |
132 | * @param pu policy url | 132 | * @param pu policy url |
133 | * @see policy_url | 133 | * @see policy_url |
134 | */ | 134 | */ |
135 | sreg_t(long fr=fields_NONE,long fo=fields_NONE,const string& pu="") | 135 | sreg_t(long fr=fields_NONE,long fo=fields_NONE,const string& pu="") |
136 | : fields_required(fr), fields_optional(fo), policy_url(pu), has_fields(0) { } | 136 | : fields_required(fr), fields_optional(fo), policy_url(pu), has_fields(0) { } |
137 | 137 | ||
138 | /** | 138 | virtual void rp_checkid_hook(basic_openid_message& om); |
139 | * Implementation of consumer's checkid hook | 139 | virtual void rp_id_res_hook(const basic_openid_message& om, |
140 | */ | 140 | const basic_openid_message& sp); |
141 | virtual void op_checkid_hook(const basic_openid_message& inm); | ||
142 | virtual void op_id_res_hook(basic_openid_message& oum); | ||
143 | |||
141 | virtual void checkid_hook(basic_openid_message& om); | 144 | virtual void checkid_hook(basic_openid_message& om); |
142 | /** | 145 | virtual void id_res_hook(const basic_openid_message& om, |
143 | * Implementation of consumer's id_res hook | 146 | const basic_openid_message& sp); |
144 | */ | 147 | virtual void checkid_hook(const basic_openid_message& inm, |
145 | virtual void id_res_hook(const basic_openid_message& om,const basic_openid_message& sp); | 148 | basic_openid_message& oum); |
146 | /** | ||
147 | * Implementation of server's checkid_hook | ||
148 | */ | ||
149 | virtual void checkid_hook(const basic_openid_message& inm,basic_openid_message& oum); | ||
150 | 149 | ||
151 | /** | 150 | /** |
152 | * Check and see if we have value for some particular field. | 151 | * Check and see if we have value for some particular field. |
153 | * @param fb field in question | 152 | * @param fb field in question |
154 | * @see fieldbit_t | 153 | * @see fieldbit_t |
155 | * @return true if the value is available | 154 | * @return true if the value is available |
156 | */ | 155 | */ |
157 | bool has_field(fieldbit_t fb) const { return has_fields&fb; } | 156 | bool has_field(fieldbit_t fb) const { return has_fields&fb; } |
158 | 157 | ||
159 | /** | 158 | /** |
160 | * Retrieve the value for a field. | 159 | * Retrieve the value for a field. |
161 | * @param fb field in question | 160 | * @param fb field in question |
162 | * @see fieldbit_t | 161 | * @see fieldbit_t |
163 | * @return field value | 162 | * @return field value |
164 | * @throw failed_lookup if no data avaialble | 163 | * @throw failed_lookup if no data avaialble |
165 | */ | 164 | */ |
166 | const string& get_field(fieldbit_t fb) const; | 165 | const string& get_field(fieldbit_t fb) const; |
167 | 166 | ||
168 | /** | 167 | /** |
169 | * Set the value for a field. | 168 | * Set the value for a field. |
170 | * @param fb field in question | 169 | * @param fb field in question |
171 | * @see fieldbit_t | 170 | * @see fieldbit_t |
172 | * @param fv field value | 171 | * @param fv field value |
173 | */ | 172 | */ |
174 | void set_field(fieldbit_t fb,const string& fv); | 173 | void set_field(fieldbit_t fb,const string& fv); |
175 | 174 | ||
176 | /** | 175 | /** |
177 | * Remove the value for a field. | 176 | * Remove the value for a field. |
178 | * @param fb field in question | 177 | * @param fb field in question |
179 | * @see fieldbit_t | 178 | * @see fieldbit_t |
180 | */ | 179 | */ |
181 | void reset_field(fieldbit_t fb); | 180 | void reset_field(fieldbit_t fb); |
182 | 181 | ||
183 | /** | 182 | /** |
184 | * Reset field data | 183 | * Reset field data |
185 | */ | 184 | */ |
186 | void clear(); | 185 | void clear(); |
187 | 186 | ||
188 | /** | 187 | /** |
189 | * Function called after parsing sreg request to set up response | 188 | * Function called after parsing sreg request to set up response |
190 | * fields. The default implementation tries to send as much fields | 189 | * fields. The default implementation tries to send as much fields |
191 | * as we have. The function is supposed to set the data and | 190 | * as we have. The function is supposed to set the data and |
192 | * fields_response. | 191 | * fields_response. |
193 | * @see fields_response | 192 | * @see fields_response |
194 | * @param pin input request parameters with "openid." prefix | 193 | * @param inm incoming openid message |
195 | * @param pout output request parameters without "openid." prefix. | 194 | * @param oum outgoing openid message |
196 | * @see checkid_hook(const params_t&,params_t&) | ||
197 | */ | 195 | */ |
198 | virtual void setup_response(const basic_openid_message& inm,basic_openid_message& oum); | 196 | virtual void setup_response(const basic_openid_message& inm, |
197 | basic_openid_message& oum); | ||
198 | |||
199 | virtual void setup_response(); | ||
199 | 200 | ||
200 | }; | 201 | }; |
201 | } | 202 | } |
202 | 203 | ||
203 | #endif /* __OPKELE_SREG_H */ | 204 | #endif /* __OPKELE_SREG_H */ |
diff --git a/lib/basic_op.cc b/lib/basic_op.cc index c89d1d7..9e2ea5a 100644 --- a/lib/basic_op.cc +++ b/lib/basic_op.cc | |||
@@ -1,328 +1,331 @@ | |||
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/uris.h> | 9 | #include <opkele/uris.h> |
10 | 10 | ||
11 | namespace opkele { | 11 | namespace opkele { |
12 | 12 | ||
13 | void basic_op::reset_vars() { | 13 | void basic_op::reset_vars() { |
14 | assoc.reset(); | 14 | assoc.reset(); |
15 | return_to.clear(); realm.clear(); | 15 | return_to.clear(); realm.clear(); |
16 | claimed_id.clear(); identity.clear(); | 16 | claimed_id.clear(); identity.clear(); |
17 | invalidate_handle.clear(); | 17 | invalidate_handle.clear(); |
18 | } | 18 | } |
19 | 19 | ||
20 | bool basic_op::has_return_to() const { | 20 | bool basic_op::has_return_to() const { |
21 | return !return_to.empty(); | 21 | return !return_to.empty(); |
22 | } | 22 | } |
23 | const string& basic_op::get_return_to() const { | 23 | const string& basic_op::get_return_to() const { |
24 | if(return_to.empty()) | 24 | if(return_to.empty()) |
25 | throw no_return_to(OPKELE_CP_ "No return_to URL provided with request"); | 25 | throw no_return_to(OPKELE_CP_ "No return_to URL provided with request"); |
26 | return return_to; | 26 | return return_to; |
27 | } | 27 | } |
28 | 28 | ||
29 | const string& basic_op::get_realm() const { | 29 | const string& basic_op::get_realm() const { |
30 | assert(!realm.empty()); | 30 | assert(!realm.empty()); |
31 | return realm; | 31 | return realm; |
32 | } | 32 | } |
33 | 33 | ||
34 | bool basic_op::has_identity() const { | 34 | bool basic_op::has_identity() const { |
35 | return !identity.empty(); | 35 | return !identity.empty(); |
36 | } | 36 | } |
37 | const string& basic_op::get_claimed_id() const { | 37 | const string& basic_op::get_claimed_id() const { |
38 | if(claimed_id.empty()) | 38 | if(claimed_id.empty()) |
39 | throw non_identity(OPKELE_CP_ "attempting to retrieve claimed_id of non-identity related request"); | 39 | throw non_identity(OPKELE_CP_ "attempting to retrieve claimed_id of non-identity related request"); |
40 | assert(!identity.empty()); | 40 | assert(!identity.empty()); |
41 | return claimed_id; | 41 | return claimed_id; |
42 | } | 42 | } |
43 | const string& basic_op::get_identity() const { | 43 | const string& basic_op::get_identity() const { |
44 | if(identity.empty()) | 44 | if(identity.empty()) |
45 | throw non_identity(OPKELE_CP_ "attempting to retrieve identity of non-identity related request"); | 45 | throw non_identity(OPKELE_CP_ "attempting to retrieve identity of non-identity related request"); |
46 | assert(!claimed_id.empty()); | 46 | assert(!claimed_id.empty()); |
47 | return identity; | 47 | return identity; |
48 | } | 48 | } |
49 | 49 | ||
50 | bool basic_op::is_id_select() const { | 50 | bool basic_op::is_id_select() const { |
51 | return identity==IDURI_SELECT20; | 51 | return identity==IDURI_SELECT20; |
52 | } | 52 | } |
53 | 53 | ||
54 | void basic_op::select_identity(const string& c,const string& i) { | 54 | void basic_op::select_identity(const string& c,const string& i) { |
55 | claimed_id = c; identity = i; | 55 | claimed_id = c; identity = i; |
56 | } | 56 | } |
57 | void basic_op::set_claimed_id(const string& c) { | 57 | void basic_op::set_claimed_id(const string& c) { |
58 | claimed_id = c; | 58 | claimed_id = c; |
59 | } | 59 | } |
60 | 60 | ||
61 | basic_openid_message& basic_op::associate( | 61 | basic_openid_message& basic_op::associate( |
62 | basic_openid_message& oum, | 62 | basic_openid_message& oum, |
63 | const basic_openid_message& inm) try { | 63 | const basic_openid_message& inm) try { |
64 | assert(inm.get_field("mode")=="associate"); | 64 | assert(inm.get_field("mode")=="associate"); |
65 | util::dh_t dh; | 65 | util::dh_t dh; |
66 | util::bignum_t c_pub; | 66 | util::bignum_t c_pub; |
67 | unsigned char key_digest[SHA256_DIGEST_LENGTH]; | 67 | unsigned char key_digest[SHA256_DIGEST_LENGTH]; |
68 | size_t d_len = 0; | 68 | size_t d_len = 0; |
69 | enum { | 69 | enum { |
70 | sess_cleartext, sess_dh_sha1, sess_dh_sha256 | 70 | sess_cleartext, sess_dh_sha1, sess_dh_sha256 |
71 | } st = sess_cleartext; | 71 | } st = sess_cleartext; |
72 | string sts = inm.get_field("session_type"); | 72 | string sts = inm.get_field("session_type"); |
73 | string ats = inm.get_field("assoc_type"); | 73 | string ats = inm.get_field("assoc_type"); |
74 | if(sts=="DH-SHA1" || sts=="DH-SHA256") { | 74 | if(sts=="DH-SHA1" || sts=="DH-SHA256") { |
75 | if(!(dh = DH_new())) | 75 | if(!(dh = DH_new())) |
76 | throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); | 76 | throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); |
77 | c_pub = util::base64_to_bignum(inm.get_field("dh_consumer_public")); | 77 | c_pub = util::base64_to_bignum(inm.get_field("dh_consumer_public")); |
78 | try { dh->p = util::base64_to_bignum(inm.get_field("dh_modulus")); | 78 | try { dh->p = util::base64_to_bignum(inm.get_field("dh_modulus")); |
79 | }catch(failed_lookup&) { | 79 | }catch(failed_lookup&) { |
80 | dh->p = util::dec_to_bignum(data::_default_p); } | 80 | dh->p = util::dec_to_bignum(data::_default_p); } |
81 | try { dh->g = util::base64_to_bignum(inm.get_field("dh_gen")); | 81 | try { dh->g = util::base64_to_bignum(inm.get_field("dh_gen")); |
82 | }catch(failed_lookup&) { | 82 | }catch(failed_lookup&) { |
83 | dh->g = util::dec_to_bignum(data::_default_g); } | 83 | dh->g = util::dec_to_bignum(data::_default_g); } |
84 | if(!DH_generate_key(dh)) | 84 | if(!DH_generate_key(dh)) |
85 | throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); | 85 | throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); |
86 | vector<unsigned char> ck(DH_size(dh)+1); | 86 | vector<unsigned char> ck(DH_size(dh)+1); |
87 | unsigned char *ckptr = &(ck.front())+1; | 87 | unsigned char *ckptr = &(ck.front())+1; |
88 | int cklen = DH_compute_key(ckptr,c_pub,dh); | 88 | int cklen = DH_compute_key(ckptr,c_pub,dh); |
89 | if(cklen<0) | 89 | if(cklen<0) |
90 | throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); | 90 | throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); |
91 | if(cklen && (*ckptr)&0x80) { | 91 | if(cklen && (*ckptr)&0x80) { |
92 | (*(--ckptr)) = 0; ++cklen; } | 92 | (*(--ckptr)) = 0; ++cklen; } |
93 | if(sts=="DH-SHA1") { | 93 | if(sts=="DH-SHA1") { |
94 | SHA1(ckptr,cklen,key_digest); d_len = SHA_DIGEST_LENGTH; | 94 | SHA1(ckptr,cklen,key_digest); d_len = SHA_DIGEST_LENGTH; |
95 | }else if(sts=="DH-SHA256") { | 95 | }else if(sts=="DH-SHA256") { |
96 | SHA256(ckptr,cklen,key_digest); d_len = SHA256_DIGEST_LENGTH; | 96 | SHA256(ckptr,cklen,key_digest); d_len = SHA256_DIGEST_LENGTH; |
97 | }else | 97 | }else |
98 | throw internal_error(OPKELE_CP_ "I thought I knew the session type"); | 98 | throw internal_error(OPKELE_CP_ "I thought I knew the session type"); |
99 | }else | 99 | }else |
100 | throw unsupported(OPKELE_CP_ "Unsupported session_type"); | 100 | throw unsupported(OPKELE_CP_ "Unsupported session_type"); |
101 | assoc_t assoc; | 101 | assoc_t assoc; |
102 | if(ats=="HMAC-SHA1") | 102 | if(ats=="HMAC-SHA1") |
103 | assoc = alloc_assoc(ats,SHA_DIGEST_LENGTH,true); | 103 | assoc = alloc_assoc(ats,SHA_DIGEST_LENGTH,true); |
104 | else if(ats=="HMAC-SHA256") | 104 | else if(ats=="HMAC-SHA256") |
105 | assoc = alloc_assoc(ats,SHA256_DIGEST_LENGTH,true); | 105 | assoc = alloc_assoc(ats,SHA256_DIGEST_LENGTH,true); |
106 | else | 106 | else |
107 | throw unsupported(OPKELE_CP_ "Unsupported assoc_type"); | 107 | throw unsupported(OPKELE_CP_ "Unsupported assoc_type"); |
108 | oum.reset_fields(); | 108 | oum.reset_fields(); |
109 | oum.set_field("ns",OIURI_OPENID20); | 109 | oum.set_field("ns",OIURI_OPENID20); |
110 | oum.set_field("assoc_type",assoc->assoc_type()); | 110 | oum.set_field("assoc_type",assoc->assoc_type()); |
111 | oum.set_field("assoc_handle",assoc->handle()); | 111 | oum.set_field("assoc_handle",assoc->handle()); |
112 | oum.set_field("expires_in",util::long_to_string(assoc->expires_in())); | 112 | oum.set_field("expires_in",util::long_to_string(assoc->expires_in())); |
113 | secret_t secret = assoc->secret(); | 113 | secret_t secret = assoc->secret(); |
114 | if(sts=="DH-SHA1" || sts=="DH-SHA256") { | 114 | if(sts=="DH-SHA1" || sts=="DH-SHA256") { |
115 | if(d_len != secret.size()) | 115 | if(d_len != secret.size()) |
116 | throw bad_input(OPKELE_CP_ "Association secret and session MAC are not of the same size"); | 116 | throw bad_input(OPKELE_CP_ "Association secret and session MAC are not of the same size"); |
117 | oum.set_field("session_type",sts); | 117 | oum.set_field("session_type",sts); |
118 | oum.set_field("dh_server_public",util::bignum_to_base64(dh->pub_key)); | 118 | oum.set_field("dh_server_public",util::bignum_to_base64(dh->pub_key)); |
119 | string b64; secret.enxor_to_base64(key_digest,b64); | 119 | string b64; secret.enxor_to_base64(key_digest,b64); |
120 | oum.set_field("enc_mac_key",b64); | 120 | oum.set_field("enc_mac_key",b64); |
121 | }else /* TODO: support cleartext over encrypted connection */ | 121 | }else /* TODO: support cleartext over encrypted connection */ |
122 | throw unsupported(OPKELE_CP_ "Unsupported session type"); | 122 | throw unsupported(OPKELE_CP_ "Unsupported session type"); |
123 | return oum; | 123 | return oum; |
124 | } catch(unsupported& u) { | 124 | } catch(unsupported& u) { |
125 | oum.reset_fields(); | 125 | oum.reset_fields(); |
126 | oum.set_field("ns",OIURI_OPENID20); | 126 | oum.set_field("ns",OIURI_OPENID20); |
127 | oum.set_field("error",u.what()); | 127 | oum.set_field("error",u.what()); |
128 | oum.set_field("error_code","unsupported-type"); | 128 | oum.set_field("error_code","unsupported-type"); |
129 | oum.set_field("session_type","DH-SHA256"); | 129 | oum.set_field("session_type","DH-SHA256"); |
130 | oum.set_field("assoc_type","HMAC-SHA256"); | 130 | oum.set_field("assoc_type","HMAC-SHA256"); |
131 | return oum; | 131 | return oum; |
132 | } | 132 | } |
133 | 133 | ||
134 | void basic_op::checkid_(const basic_openid_message& inm, | 134 | void basic_op::checkid_(const basic_openid_message& inm, |
135 | extension_t *ext) { | 135 | extension_t *ext) { |
136 | reset_vars(); | 136 | reset_vars(); |
137 | string mode = inm.get_field("mode"); | 137 | string mode = inm.get_field("mode"); |
138 | if(mode=="checkid_setup") | 138 | if(mode=="checkid_setup") |
139 | mode = mode_checkid_setup; | 139 | mode = mode_checkid_setup; |
140 | else if(mode=="checkid_immediate") | 140 | else if(mode=="checkid_immediate") |
141 | mode = mode_checkid_immediate; | 141 | mode = mode_checkid_immediate; |
142 | else | 142 | else |
143 | throw bad_input(OPKELE_CP_ "Invalid checkid_* mode"); | 143 | throw bad_input(OPKELE_CP_ "Invalid checkid_* mode"); |
144 | try { | 144 | try { |
145 | assoc = retrieve_assoc(invalidate_handle=inm.get_field("assoc_handle")); | 145 | assoc = retrieve_assoc(invalidate_handle=inm.get_field("assoc_handle")); |
146 | invalidate_handle.clear(); | 146 | invalidate_handle.clear(); |
147 | }catch(failed_lookup&) { | 147 | }catch(failed_lookup&) { |
148 | // no handle specified or no valid assoc found, go dumb | 148 | // no handle specified or no valid assoc found, go dumb |
149 | assoc = alloc_assoc("HMAC-SHA256",SHA256_DIGEST_LENGTH,true); | 149 | assoc = alloc_assoc("HMAC-SHA256",SHA256_DIGEST_LENGTH,true); |
150 | } | 150 | } |
151 | try { | 151 | try { |
152 | openid2 = (inm.get_field("ns")==OIURI_OPENID20); | 152 | openid2 = (inm.get_field("ns")==OIURI_OPENID20); |
153 | }catch(failed_lookup&) { openid2 = false; } | 153 | }catch(failed_lookup&) { openid2 = false; } |
154 | try { | 154 | try { |
155 | return_to = inm.get_field("return_to"); | 155 | return_to = inm.get_field("return_to"); |
156 | }catch(failed_lookup&) { } | 156 | }catch(failed_lookup&) { } |
157 | if(openid2) { | 157 | if(openid2) { |
158 | try { | 158 | try { |
159 | realm = inm.get_field("realm"); | 159 | realm = inm.get_field("realm"); |
160 | }catch(failed_lookup&) { | 160 | }catch(failed_lookup&) { |
161 | try { | 161 | try { |
162 | realm = inm.get_field("trust_root"); | 162 | realm = inm.get_field("trust_root"); |
163 | }catch(failed_lookup&) { | 163 | }catch(failed_lookup&) { |
164 | if(return_to.empty()) | 164 | if(return_to.empty()) |
165 | throw bad_input(OPKELE_CP_ | 165 | throw bad_input(OPKELE_CP_ |
166 | "Both realm and return_to are unset"); | 166 | "Both realm and return_to are unset"); |
167 | realm = return_to; | 167 | realm = return_to; |
168 | } | 168 | } |
169 | } | 169 | } |
170 | }else{ | 170 | }else{ |
171 | try { | 171 | try { |
172 | realm = inm.get_field("trust_root"); | 172 | realm = inm.get_field("trust_root"); |
173 | }catch(failed_lookup&) { | 173 | }catch(failed_lookup&) { |
174 | if(return_to.empty()) | 174 | if(return_to.empty()) |
175 | throw bad_input(OPKELE_CP_ | 175 | throw bad_input(OPKELE_CP_ |
176 | "Both realm and return_to are unset"); | 176 | "Both realm and return_to are unset"); |
177 | realm = return_to; | 177 | realm = return_to; |
178 | } | 178 | } |
179 | } | 179 | } |
180 | try { | 180 | try { |
181 | identity = inm.get_field("identity"); | 181 | identity = inm.get_field("identity"); |
182 | try { | 182 | try { |
183 | claimed_id = inm.get_field("claimed_id"); | 183 | claimed_id = inm.get_field("claimed_id"); |
184 | }catch(failed_lookup&) { | 184 | }catch(failed_lookup&) { |
185 | if(openid2) | 185 | if(openid2) |
186 | throw bad_input(OPKELE_CP_ | 186 | throw bad_input(OPKELE_CP_ |
187 | "claimed_id and identity must be either both present or both absent"); | 187 | "claimed_id and identity must be either both present or both absent"); |
188 | claimed_id = identity; | 188 | claimed_id = identity; |
189 | } | 189 | } |
190 | }catch(failed_lookup&) { | 190 | }catch(failed_lookup&) { |
191 | if(openid2 && inm.has_field("claimed_id")) | 191 | if(openid2 && inm.has_field("claimed_id")) |
192 | throw bad_input(OPKELE_CP_ | 192 | throw bad_input(OPKELE_CP_ |
193 | "claimed_id and identity must be either both present or both absent"); | 193 | "claimed_id and identity must be either both present or both absent"); |
194 | } | 194 | } |
195 | verify_return_to(); | 195 | verify_return_to(); |
196 | if(ext) ext->op_checkid_hook(inm); | ||
196 | } | 197 | } |
197 | 198 | ||
198 | basic_openid_message& basic_op::id_res(basic_openid_message& om) { | 199 | basic_openid_message& basic_op::id_res(basic_openid_message& om, |
200 | extension_t *ext) { | ||
199 | assert(assoc); | 201 | assert(assoc); |
200 | assert(!return_to.empty()); | 202 | assert(!return_to.empty()); |
201 | assert(!is_id_select()); | 203 | assert(!is_id_select()); |
202 | time_t now = time(0); | 204 | time_t now = time(0); |
203 | struct tm gmt; gmtime_r(&now,&gmt); | 205 | struct tm gmt; gmtime_r(&now,&gmt); |
204 | char w3timestr[24]; | 206 | char w3timestr[24]; |
205 | if(!strftime(w3timestr,sizeof(w3timestr),"%Y-%m-%dT%H:%M:%SZ",&gmt)) | 207 | if(!strftime(w3timestr,sizeof(w3timestr),"%Y-%m-%dT%H:%M:%SZ",&gmt)) |
206 | throw failed_conversion(OPKELE_CP_ | 208 | throw failed_conversion(OPKELE_CP_ |
207 | "Failed to build time string for nonce" ); | 209 | "Failed to build time string for nonce" ); |
208 | om.set_field("ns",OIURI_OPENID20); | 210 | om.set_field("ns",OIURI_OPENID20); |
209 | om.set_field("mode","id_res"); | 211 | om.set_field("mode","id_res"); |
210 | om.set_field("op_endpoint",get_op_endpoint()); | 212 | om.set_field("op_endpoint",get_op_endpoint()); |
211 | string ats = "ns,mode,op_endpoint,return_to,response_nonce," | 213 | string ats = "ns,mode,op_endpoint,return_to,response_nonce," |
212 | "assoc_handle,signed"; | 214 | "assoc_handle,signed"; |
213 | if(!identity.empty()) { | 215 | if(!identity.empty()) { |
214 | om.set_field("identity",identity); | 216 | om.set_field("identity",identity); |
215 | om.set_field("claimed_id",claimed_id); | 217 | om.set_field("claimed_id",claimed_id); |
216 | ats += ",identity,claimed_id"; | 218 | ats += ",identity,claimed_id"; |
217 | } | 219 | } |
218 | om.set_field("return_to",return_to); | 220 | om.set_field("return_to",return_to); |
219 | string nonce = w3timestr; | 221 | string nonce = w3timestr; |
220 | om.set_field("response_nonce",alloc_nonce(nonce,assoc->stateless())); | 222 | om.set_field("response_nonce",alloc_nonce(nonce,assoc->stateless())); |
221 | if(!invalidate_handle.empty()) { | 223 | if(!invalidate_handle.empty()) { |
222 | om.set_field("invalidate_handle",invalidate_handle); | 224 | om.set_field("invalidate_handle",invalidate_handle); |
223 | ats += ",invalidate_handle"; | 225 | ats += ",invalidate_handle"; |
224 | } | 226 | } |
225 | om.set_field("assoc_handle",assoc->handle()); | 227 | om.set_field("assoc_handle",assoc->handle()); |
226 | om.add_to_signed(ats); | 228 | om.add_to_signed(ats); |
229 | if(ext) ext->op_id_res_hook(om); | ||
227 | om.set_field("sig",util::base64_signature(assoc,om)); | 230 | om.set_field("sig",util::base64_signature(assoc,om)); |
228 | return om; | 231 | return om; |
229 | } | 232 | } |
230 | 233 | ||
231 | basic_openid_message& basic_op::cancel(basic_openid_message& om) { | 234 | basic_openid_message& basic_op::cancel(basic_openid_message& om) { |
232 | assert(!return_to.empty()); | 235 | assert(!return_to.empty()); |
233 | om.set_field("ns",OIURI_OPENID20); | 236 | om.set_field("ns",OIURI_OPENID20); |
234 | om.set_field("mode","cancel"); | 237 | om.set_field("mode","cancel"); |
235 | return om; | 238 | return om; |
236 | } | 239 | } |
237 | 240 | ||
238 | basic_openid_message& basic_op::error(basic_openid_message& om, | 241 | basic_openid_message& basic_op::error(basic_openid_message& om, |
239 | const string& error,const string& contact, | 242 | const string& error,const string& contact, |
240 | const string& reference ) { | 243 | const string& reference ) { |
241 | assert(!return_to.empty()); | 244 | assert(!return_to.empty()); |
242 | om.set_field("ns",OIURI_OPENID20); | 245 | om.set_field("ns",OIURI_OPENID20); |
243 | om.set_field("mode","error"); | 246 | om.set_field("mode","error"); |
244 | om.set_field("error",error); | 247 | om.set_field("error",error); |
245 | om.set_field("contact",contact); | 248 | om.set_field("contact",contact); |
246 | om.set_field("reference",reference); | 249 | om.set_field("reference",reference); |
247 | return om; | 250 | return om; |
248 | } | 251 | } |
249 | 252 | ||
250 | basic_openid_message& basic_op::setup_needed( | 253 | basic_openid_message& basic_op::setup_needed( |
251 | basic_openid_message& oum,const basic_openid_message& inm) { | 254 | basic_openid_message& oum,const basic_openid_message& inm) { |
252 | assert(mode==mode_checkid_immediate); | 255 | assert(mode==mode_checkid_immediate); |
253 | assert(!return_to.empty()); | 256 | assert(!return_to.empty()); |
254 | if(openid2) { | 257 | if(openid2) { |
255 | oum.set_field("ns",OIURI_OPENID20); | 258 | oum.set_field("ns",OIURI_OPENID20); |
256 | oum.set_field("mode","setup_needed"); | 259 | oum.set_field("mode","setup_needed"); |
257 | }else{ | 260 | }else{ |
258 | oum.set_field("mode","id_res"); | 261 | oum.set_field("mode","id_res"); |
259 | static const string setupmode = "checkid_setup"; | 262 | static const string setupmode = "checkid_setup"; |
260 | oum.set_field("user_setup_url", | 263 | oum.set_field("user_setup_url", |
261 | util::change_mode_message_proxy(inm,setupmode) | 264 | util::change_mode_message_proxy(inm,setupmode) |
262 | .append_query(get_op_endpoint())); | 265 | .append_query(get_op_endpoint())); |
263 | } | 266 | } |
264 | return oum; | 267 | return oum; |
265 | } | 268 | } |
266 | 269 | ||
267 | basic_openid_message& basic_op::check_authentication( | 270 | basic_openid_message& basic_op::check_authentication( |
268 | basic_openid_message& oum, | 271 | basic_openid_message& oum, |
269 | const basic_openid_message& inm) try { | 272 | const basic_openid_message& inm) try { |
270 | assert(inm.get_field("mode")=="check_authentication"); | 273 | assert(inm.get_field("mode")=="check_authentication"); |
271 | oum.reset_fields(); | 274 | oum.reset_fields(); |
272 | oum.set_field("ns",OIURI_OPENID20); | 275 | oum.set_field("ns",OIURI_OPENID20); |
273 | bool o2; | 276 | bool o2; |
274 | try { | 277 | try { |
275 | o2 = (inm.get_field("ns")==OIURI_OPENID20); | 278 | o2 = (inm.get_field("ns")==OIURI_OPENID20); |
276 | }catch(failed_lookup&) { o2 = false; } | 279 | }catch(failed_lookup&) { o2 = false; } |
277 | string nonce; | 280 | string nonce; |
278 | if(o2) { | 281 | if(o2) { |
279 | try { | 282 | try { |
280 | if(!check_nonce(nonce = inm.get_field("response_nonce"))) | 283 | if(!check_nonce(nonce = inm.get_field("response_nonce"))) |
281 | throw failed_check_authentication(OPKELE_CP_ "Invalid nonce"); | 284 | throw failed_check_authentication(OPKELE_CP_ "Invalid nonce"); |
282 | }catch(failed_lookup&) { | 285 | }catch(failed_lookup&) { |
283 | throw failed_check_authentication(OPKELE_CP_ "No nonce provided with check_authentication request"); | 286 | throw failed_check_authentication(OPKELE_CP_ "No nonce provided with check_authentication request"); |
284 | } | 287 | } |
285 | } | 288 | } |
286 | try { | 289 | try { |
287 | assoc = retrieve_assoc(inm.get_field("assoc_handle")); | 290 | assoc = retrieve_assoc(inm.get_field("assoc_handle")); |
288 | if(!assoc->stateless()) | 291 | if(!assoc->stateless()) |
289 | throw failed_check_authentication(OPKELE_CP_ "Will not do check_authentication on a stateful handle"); | 292 | throw failed_check_authentication(OPKELE_CP_ "Will not do check_authentication on a stateful handle"); |
290 | }catch(failed_lookup&) { | 293 | }catch(failed_lookup&) { |
291 | throw failed_check_authentication(OPKELE_CP_ "No assoc_handle or invalid assoc_handle specified with check_authentication request"); | 294 | throw failed_check_authentication(OPKELE_CP_ "No assoc_handle or invalid assoc_handle specified with check_authentication request"); |
292 | } | 295 | } |
293 | static const string idresmode = "id_res"; | 296 | static const string idresmode = "id_res"; |
294 | try { | 297 | try { |
295 | if(util::base64_signature(assoc,util::change_mode_message_proxy(inm,idresmode))!=inm.get_field("sig")) | 298 | if(util::base64_signature(assoc,util::change_mode_message_proxy(inm,idresmode))!=inm.get_field("sig")) |
296 | throw failed_check_authentication(OPKELE_CP_ "Signature mismatch"); | 299 | throw failed_check_authentication(OPKELE_CP_ "Signature mismatch"); |
297 | }catch(failed_lookup&) { | 300 | }catch(failed_lookup&) { |
298 | throw failed_check_authentication(OPKELE_CP_ "failed to calculate signature"); | 301 | throw failed_check_authentication(OPKELE_CP_ "failed to calculate signature"); |
299 | } | 302 | } |
300 | oum.set_field("is_valid","true"); | 303 | oum.set_field("is_valid","true"); |
301 | try { | 304 | try { |
302 | string h = inm.get_field("invalidate_handle"); | 305 | string h = inm.get_field("invalidate_handle"); |
303 | try { | 306 | try { |
304 | assoc_t ih = retrieve_assoc(h); | 307 | assoc_t ih = retrieve_assoc(h); |
305 | }catch(invalid_handle& ih) { | 308 | }catch(invalid_handle& ih) { |
306 | oum.set_field("invalidate_handle",h); | 309 | oum.set_field("invalidate_handle",h); |
307 | }catch(failed_lookup& ih) { | 310 | }catch(failed_lookup& ih) { |
308 | oum.set_field("invalidate_handle",h); | 311 | oum.set_field("invalidate_handle",h); |
309 | } | 312 | } |
310 | }catch(failed_lookup&) { } | 313 | }catch(failed_lookup&) { } |
311 | if(o2) { | 314 | if(o2) { |
312 | assert(!nonce.empty()); | 315 | assert(!nonce.empty()); |
313 | invalidate_nonce(nonce); | 316 | invalidate_nonce(nonce); |
314 | } | 317 | } |
315 | return oum; | 318 | return oum; |
316 | }catch(failed_check_authentication& ) { | 319 | }catch(failed_check_authentication& ) { |
317 | oum.set_field("is_valid","false"); | 320 | oum.set_field("is_valid","false"); |
318 | return oum; | 321 | return oum; |
319 | } | 322 | } |
320 | 323 | ||
321 | void basic_op::verify_return_to() { | 324 | void basic_op::verify_return_to() { |
322 | if(realm.find('#')!=string::npos) | 325 | if(realm.find('#')!=string::npos) |
323 | throw opkele::bad_realm(OPKELE_CP_ "authentication realm contains URI fragment"); | 326 | throw opkele::bad_realm(OPKELE_CP_ "authentication realm contains URI fragment"); |
324 | if(!util::uri_matches_realm(return_to,realm)) | 327 | if(!util::uri_matches_realm(return_to,realm)) |
325 | throw bad_return_to(OPKELE_CP_ "return_to URL doesn't match realm"); | 328 | throw bad_return_to(OPKELE_CP_ "return_to URL doesn't match realm"); |
326 | } | 329 | } |
327 | 330 | ||
328 | } | 331 | } |
diff --git a/lib/basic_rp.cc b/lib/basic_rp.cc index a884583..bd45d99 100644 --- a/lib/basic_rp.cc +++ b/lib/basic_rp.cc | |||
@@ -1,297 +1,297 @@ | |||
1 | #include <openssl/sha.h> | 1 | #include <openssl/sha.h> |
2 | #include <openssl/hmac.h> | 2 | #include <openssl/hmac.h> |
3 | #include <opkele/basic_rp.h> | 3 | #include <opkele/basic_rp.h> |
4 | #include <opkele/exception.h> | 4 | #include <opkele/exception.h> |
5 | #include <opkele/uris.h> | 5 | #include <opkele/uris.h> |
6 | #include <opkele/data.h> | 6 | #include <opkele/data.h> |
7 | #include <opkele/util.h> | 7 | #include <opkele/util.h> |
8 | #include <opkele/curl.h> | 8 | #include <opkele/curl.h> |
9 | 9 | ||
10 | namespace opkele { | 10 | namespace opkele { |
11 | 11 | ||
12 | static void dh_get_secret( | 12 | static void dh_get_secret( |
13 | secret_t& secret, const basic_openid_message& om, | 13 | secret_t& secret, const basic_openid_message& om, |
14 | const char *exp_assoc, const char *exp_sess, | 14 | const char *exp_assoc, const char *exp_sess, |
15 | util::dh_t& dh, | 15 | util::dh_t& dh, |
16 | size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*), | 16 | size_t d_len, unsigned char *(*d_fun)(const unsigned char*,size_t,unsigned char*), |
17 | size_t exp_s_len) try { | 17 | size_t exp_s_len) try { |
18 | if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess) | 18 | if(om.get_field("assoc_type")!=exp_assoc || om.get_field("session_type")!=exp_sess) |
19 | throw bad_input(OPKELE_CP_ "Unexpected associate response"); | 19 | throw bad_input(OPKELE_CP_ "Unexpected associate response"); |
20 | util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public")); | 20 | util::bignum_t s_pub = util::base64_to_bignum(om.get_field("dh_server_public")); |
21 | vector<unsigned char> ck(DH_size(dh)+1); | 21 | vector<unsigned char> ck(DH_size(dh)+1); |
22 | unsigned char *ckptr = &(ck.front())+1; | 22 | unsigned char *ckptr = &(ck.front())+1; |
23 | int cklen = DH_compute_key(ckptr,s_pub,dh); | 23 | int cklen = DH_compute_key(ckptr,s_pub,dh); |
24 | if(cklen<0) | 24 | if(cklen<0) |
25 | throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); | 25 | throw exception_openssl(OPKELE_CP_ "failed to DH_compute_key()"); |
26 | if(cklen && (*ckptr)&0x80) { | 26 | if(cklen && (*ckptr)&0x80) { |
27 | (*(--ckptr))=0; ++cklen; } | 27 | (*(--ckptr))=0; ++cklen; } |
28 | unsigned char key_digest[d_len]; | 28 | unsigned char key_digest[d_len]; |
29 | secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key")); | 29 | secret.enxor_from_base64((*d_fun)(ckptr,cklen,key_digest),om.get_field("enc_mac_key")); |
30 | if(secret.size()!=exp_s_len) | 30 | if(secret.size()!=exp_s_len) |
31 | throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type"); | 31 | throw bad_input(OPKELE_CP_ "Secret length isn't consistent with association type"); |
32 | }catch(opkele::failed_lookup& ofl) { | 32 | }catch(opkele::failed_lookup& ofl) { |
33 | throw bad_input(OPKELE_CP_ "Incoherent response from OP"); | 33 | throw bad_input(OPKELE_CP_ "Incoherent response from OP"); |
34 | } OPKELE_RETHROW | 34 | } OPKELE_RETHROW |
35 | 35 | ||
36 | static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) { | 36 | static void direct_request(basic_openid_message& oum,const basic_openid_message& inm,const string& OP) { |
37 | util::curl_pick_t curl = util::curl_pick_t::easy_init(); | 37 | util::curl_pick_t curl = util::curl_pick_t::easy_init(); |
38 | if(!curl) | 38 | if(!curl) |
39 | throw exception_curl(OPKELE_CP_ "failed to initialize curl"); | 39 | throw exception_curl(OPKELE_CP_ "failed to initialize curl"); |
40 | string request = inm.query_string(); | 40 | string request = inm.query_string(); |
41 | CURLcode r; | 41 | CURLcode r; |
42 | (r=curl.misc_sets()) | 42 | (r=curl.misc_sets()) |
43 | || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str())) | 43 | || (r=curl.easy_setopt(CURLOPT_URL,OP.c_str())) |
44 | || (r=curl.easy_setopt(CURLOPT_POST,1)) | 44 | || (r=curl.easy_setopt(CURLOPT_POST,1)) |
45 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) | 45 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDS,request.data())) |
46 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) | 46 | || (r=curl.easy_setopt(CURLOPT_POSTFIELDSIZE,request.length())) |
47 | || (r=curl.set_write()); | 47 | || (r=curl.set_write()); |
48 | if(r) | 48 | if(r) |
49 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); | 49 | throw exception_curl(OPKELE_CP_ "failed to set curly options",r); |
50 | if( (r=curl.easy_perform()) ) | 50 | if( (r=curl.easy_perform()) ) |
51 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); | 51 | throw exception_curl(OPKELE_CP_ "failed to perform curly request",r); |
52 | oum.from_keyvalues(curl.response); | 52 | oum.from_keyvalues(curl.response); |
53 | } | 53 | } |
54 | 54 | ||
55 | 55 | ||
56 | assoc_t basic_RP::associate(const string& OP) { | 56 | assoc_t basic_RP::associate(const string& OP) { |
57 | util::dh_t dh = DH_new(); | 57 | util::dh_t dh = DH_new(); |
58 | if(!dh) | 58 | if(!dh) |
59 | throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); | 59 | throw exception_openssl(OPKELE_CP_ "failed to DH_new()"); |
60 | dh->p = util::dec_to_bignum(data::_default_p); | 60 | dh->p = util::dec_to_bignum(data::_default_p); |
61 | dh->g = util::dec_to_bignum(data::_default_g); | 61 | dh->g = util::dec_to_bignum(data::_default_g); |
62 | if(!DH_generate_key(dh)) | 62 | if(!DH_generate_key(dh)) |
63 | throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); | 63 | throw exception_openssl(OPKELE_CP_ "failed to DH_generate_key()"); |
64 | openid_message_t req; | 64 | openid_message_t req; |
65 | req.set_field("ns",OIURI_OPENID20); | 65 | req.set_field("ns",OIURI_OPENID20); |
66 | req.set_field("mode","associate"); | 66 | req.set_field("mode","associate"); |
67 | req.set_field("dh_modulus",util::bignum_to_base64(dh->p)); | 67 | req.set_field("dh_modulus",util::bignum_to_base64(dh->p)); |
68 | req.set_field("dh_gen",util::bignum_to_base64(dh->g)); | 68 | req.set_field("dh_gen",util::bignum_to_base64(dh->g)); |
69 | req.set_field("dh_consumer_public",util::bignum_to_base64(dh->pub_key)); | 69 | req.set_field("dh_consumer_public",util::bignum_to_base64(dh->pub_key)); |
70 | openid_message_t res; | 70 | openid_message_t res; |
71 | req.set_field("assoc_type","HMAC-SHA256"); | 71 | req.set_field("assoc_type","HMAC-SHA256"); |
72 | req.set_field("session_type","DH-SHA256"); | 72 | req.set_field("session_type","DH-SHA256"); |
73 | secret_t secret; | 73 | secret_t secret; |
74 | int expires_in; | 74 | int expires_in; |
75 | try { | 75 | try { |
76 | direct_request(res,req,OP); | 76 | direct_request(res,req,OP); |
77 | dh_get_secret( secret, res, | 77 | dh_get_secret( secret, res, |
78 | "HMAC-SHA256", "DH-SHA256", | 78 | "HMAC-SHA256", "DH-SHA256", |
79 | dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH ); | 79 | dh, SHA256_DIGEST_LENGTH, SHA256, SHA256_DIGEST_LENGTH ); |
80 | expires_in = util::string_to_long(res.get_field("expires_in")); | 80 | expires_in = util::string_to_long(res.get_field("expires_in")); |
81 | }catch(exception& e) { | 81 | }catch(exception& e) { |
82 | try { | 82 | try { |
83 | req.set_field("assoc_type","HMAC-SHA1"); | 83 | req.set_field("assoc_type","HMAC-SHA1"); |
84 | req.set_field("session_type","DH-SHA1"); | 84 | req.set_field("session_type","DH-SHA1"); |
85 | direct_request(res,req,OP); | 85 | direct_request(res,req,OP); |
86 | dh_get_secret( secret, res, | 86 | dh_get_secret( secret, res, |
87 | "HMAC-SHA1", "DH-SHA1", | 87 | "HMAC-SHA1", "DH-SHA1", |
88 | dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH ); | 88 | dh, SHA_DIGEST_LENGTH, SHA1, SHA_DIGEST_LENGTH ); |
89 | expires_in = util::string_to_long(res.get_field("expires_in")); | 89 | expires_in = util::string_to_long(res.get_field("expires_in")); |
90 | }catch(bad_input& e) { | 90 | }catch(bad_input& e) { |
91 | throw dumb_RP(OPKELE_CP_ "OP failed to supply an association"); | 91 | throw dumb_RP(OPKELE_CP_ "OP failed to supply an association"); |
92 | } | 92 | } |
93 | } | 93 | } |
94 | return store_assoc( | 94 | return store_assoc( |
95 | OP, res.get_field("assoc_handle"), | 95 | OP, res.get_field("assoc_handle"), |
96 | res.get_field("assoc_type"), secret, | 96 | res.get_field("assoc_type"), secret, |
97 | expires_in ); | 97 | expires_in ); |
98 | } | 98 | } |
99 | 99 | ||
100 | basic_openid_message& basic_RP::checkid_( | 100 | basic_openid_message& basic_RP::checkid_( |
101 | basic_openid_message& rv, | 101 | basic_openid_message& rv, |
102 | mode_t mode, | 102 | mode_t mode, |
103 | const string& return_to,const string& realm, | 103 | const string& return_to,const string& realm, |
104 | extension_t *ext) { | 104 | extension_t *ext) { |
105 | rv.reset_fields(); | 105 | rv.reset_fields(); |
106 | rv.set_field("ns",OIURI_OPENID20); | 106 | rv.set_field("ns",OIURI_OPENID20); |
107 | if(mode==mode_checkid_immediate) | 107 | if(mode==mode_checkid_immediate) |
108 | rv.set_field("mode","checkid_immediate"); | 108 | rv.set_field("mode","checkid_immediate"); |
109 | else if(mode==mode_checkid_setup) | 109 | else if(mode==mode_checkid_setup) |
110 | rv.set_field("mode","checkid_setup"); | 110 | rv.set_field("mode","checkid_setup"); |
111 | else | 111 | else |
112 | throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); | 112 | throw bad_input(OPKELE_CP_ "unknown checkid_* mode"); |
113 | if(realm.empty() && return_to.empty()) | 113 | if(realm.empty() && return_to.empty()) |
114 | throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty"); | 114 | throw bad_input(OPKELE_CP_ "At least one of realm and return_to must be non-empty"); |
115 | if(!realm.empty()) { | 115 | if(!realm.empty()) { |
116 | rv.set_field("realm",realm); | 116 | rv.set_field("realm",realm); |
117 | rv.set_field("trust_root",realm); | 117 | rv.set_field("trust_root",realm); |
118 | } | 118 | } |
119 | if(!return_to.empty()) | 119 | if(!return_to.empty()) |
120 | rv.set_field("return_to",return_to); | 120 | rv.set_field("return_to",return_to); |
121 | const openid_endpoint_t& ep = get_endpoint(); | 121 | const openid_endpoint_t& ep = get_endpoint(); |
122 | rv.set_field("claimed_id",ep.claimed_id); | 122 | rv.set_field("claimed_id",ep.claimed_id); |
123 | rv.set_field("identity",ep.local_id); | 123 | rv.set_field("identity",ep.local_id); |
124 | try { | 124 | try { |
125 | rv.set_field("assoc_handle",find_assoc(ep.uri)->handle()); | 125 | rv.set_field("assoc_handle",find_assoc(ep.uri)->handle()); |
126 | }catch(dumb_RP& drp) { | 126 | }catch(dumb_RP& drp) { |
127 | }catch(failed_lookup& fl) { | 127 | }catch(failed_lookup& fl) { |
128 | try { | 128 | try { |
129 | rv.set_field("assoc_handle",associate(ep.uri)->handle()); | 129 | rv.set_field("assoc_handle",associate(ep.uri)->handle()); |
130 | }catch(dumb_RP& drp) { } | 130 | }catch(dumb_RP& drp) { } |
131 | } OPKELE_RETHROW | 131 | } OPKELE_RETHROW |
132 | if(ext) ext->checkid_hook(rv); | 132 | if(ext) ext->rp_checkid_hook(rv); |
133 | return rv; | 133 | return rv; |
134 | } | 134 | } |
135 | 135 | ||
136 | class signed_part_message_proxy : public basic_openid_message { | 136 | class signed_part_message_proxy : public basic_openid_message { |
137 | public: | 137 | public: |
138 | const basic_openid_message& x; | 138 | const basic_openid_message& x; |
139 | set<string> signeds; | 139 | set<string> signeds; |
140 | 140 | ||
141 | signed_part_message_proxy(const basic_openid_message& xx) : x(xx) { | 141 | signed_part_message_proxy(const basic_openid_message& xx) : x(xx) { |
142 | const string& slist = x.get_field("signed"); | 142 | const string& slist = x.get_field("signed"); |
143 | string::size_type p = 0; | 143 | string::size_type p = 0; |
144 | while(true) { | 144 | while(true) { |
145 | string::size_type co = slist.find(',',p); | 145 | string::size_type co = slist.find(',',p); |
146 | string f = (co==string::npos) | 146 | string f = (co==string::npos) |
147 | ?slist.substr(p):slist.substr(p,co-p); | 147 | ?slist.substr(p):slist.substr(p,co-p); |
148 | signeds.insert(f); | 148 | signeds.insert(f); |
149 | if(co==string::npos) break; | 149 | if(co==string::npos) break; |
150 | p = co+1; | 150 | p = co+1; |
151 | } | 151 | } |
152 | } | 152 | } |
153 | 153 | ||
154 | bool has_field(const string& n) const { | 154 | bool has_field(const string& n) const { |
155 | return signeds.find(n)!=signeds.end() && x.has_field(n); } | 155 | return signeds.find(n)!=signeds.end() && x.has_field(n); } |
156 | const string& get_field(const string& n) const { | 156 | const string& get_field(const string& n) const { |
157 | if(signeds.find(n)==signeds.end()) | 157 | if(signeds.find(n)==signeds.end()) |
158 | throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed"); | 158 | throw failed_lookup(OPKELE_CP_ "The field isn't known to be signed"); |
159 | return x.get_field(n); } | 159 | return x.get_field(n); } |
160 | 160 | ||
161 | fields_iterator fields_begin() const { | 161 | fields_iterator fields_begin() const { |
162 | return signeds.begin(); } | 162 | return signeds.begin(); } |
163 | fields_iterator fields_end() const { | 163 | fields_iterator fields_end() const { |
164 | return signeds.end(); } | 164 | return signeds.end(); } |
165 | }; | 165 | }; |
166 | 166 | ||
167 | static void parse_query(const string& u,string::size_type q, | 167 | static void parse_query(const string& u,string::size_type q, |
168 | map<string,string>& p) { | 168 | map<string,string>& p) { |
169 | if(q==string::npos) | 169 | if(q==string::npos) |
170 | return; | 170 | return; |
171 | assert(u[q]=='?'); | 171 | assert(u[q]=='?'); |
172 | ++q; | 172 | ++q; |
173 | string::size_type l = u.size(); | 173 | string::size_type l = u.size(); |
174 | while(q<l) { | 174 | while(q<l) { |
175 | string::size_type eq = u.find('=',q); | 175 | string::size_type eq = u.find('=',q); |
176 | string::size_type am = u.find('&',q); | 176 | string::size_type am = u.find('&',q); |
177 | if(am==string::npos) { | 177 | if(am==string::npos) { |
178 | if(eq==string::npos) { | 178 | if(eq==string::npos) { |
179 | p[""] = u.substr(q); | 179 | p[""] = u.substr(q); |
180 | }else{ | 180 | }else{ |
181 | p[u.substr(q,eq-q)] = u.substr(eq+1); | 181 | p[u.substr(q,eq-q)] = u.substr(eq+1); |
182 | } | 182 | } |
183 | break; | 183 | break; |
184 | }else{ | 184 | }else{ |
185 | if(eq==string::npos || eq>am) { | 185 | if(eq==string::npos || eq>am) { |
186 | p[""] = u.substr(q,eq-q); | 186 | p[""] = u.substr(q,eq-q); |
187 | }else{ | 187 | }else{ |
188 | p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1); | 188 | p[u.substr(q,eq-q)] = u.substr(eq+1,am-eq-1); |
189 | } | 189 | } |
190 | q = ++am; | 190 | q = ++am; |
191 | } | 191 | } |
192 | } | 192 | } |
193 | } | 193 | } |
194 | 194 | ||
195 | void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) { | 195 | void basic_RP::id_res(const basic_openid_message& om,extension_t *ext) { |
196 | bool o2 = om.has_field("ns") | 196 | bool o2 = om.has_field("ns") |
197 | && om.get_field("ns")==OIURI_OPENID20; | 197 | && om.get_field("ns")==OIURI_OPENID20; |
198 | if( (!o2) && om.has_field("user_setup_url")) | 198 | if( (!o2) && om.has_field("user_setup_url")) |
199 | throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided", | 199 | throw id_res_setup(OPKELE_CP_ "assertion failed, setup url provided", |
200 | om.get_field("user_setup_url")); | 200 | om.get_field("user_setup_url")); |
201 | string m = om.get_field("mode"); | 201 | string m = om.get_field("mode"); |
202 | if(o2 && m=="setup_needed") | 202 | if(o2 && m=="setup_needed") |
203 | throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided"); | 203 | throw id_res_setup(OPKELE_CP_ "setup needed, no setup url provided"); |
204 | if(m=="cancel") | 204 | if(m=="cancel") |
205 | throw id_res_cancel(OPKELE_CP_ "authentication cancelled"); | 205 | throw id_res_cancel(OPKELE_CP_ "authentication cancelled"); |
206 | bool go_dumb=false; | 206 | bool go_dumb=false; |
207 | try { | 207 | try { |
208 | string OP = o2 | 208 | string OP = o2 |
209 | ?om.get_field("op_endpoint") | 209 | ?om.get_field("op_endpoint") |
210 | :get_endpoint().uri; | 210 | :get_endpoint().uri; |
211 | assoc_t assoc = retrieve_assoc( | 211 | assoc_t assoc = retrieve_assoc( |
212 | OP,om.get_field("assoc_handle")); | 212 | OP,om.get_field("assoc_handle")); |
213 | if(om.get_field("sig")!=util::base64_signature(assoc,om)) | 213 | if(om.get_field("sig")!=util::base64_signature(assoc,om)) |
214 | throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); | 214 | throw id_res_mismatch(OPKELE_CP_ "signature mismatch"); |
215 | }catch(dumb_RP& drp) { | 215 | }catch(dumb_RP& drp) { |
216 | go_dumb=true; | 216 | go_dumb=true; |
217 | }catch(failed_lookup& e) { | 217 | }catch(failed_lookup& e) { |
218 | go_dumb=true; | 218 | go_dumb=true; |
219 | } OPKELE_RETHROW | 219 | } OPKELE_RETHROW |
220 | if(go_dumb) { | 220 | if(go_dumb) { |
221 | try { | 221 | try { |
222 | string OP = o2 | 222 | string OP = o2 |
223 | ?om.get_field("op_endpoint") | 223 | ?om.get_field("op_endpoint") |
224 | :get_endpoint().uri; | 224 | :get_endpoint().uri; |
225 | check_authentication(OP,om); | 225 | check_authentication(OP,om); |
226 | }catch(failed_check_authentication& fca) { | 226 | }catch(failed_check_authentication& fca) { |
227 | throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); | 227 | throw id_res_failed(OPKELE_CP_ "failed to check_authentication()"); |
228 | } OPKELE_RETHROW | 228 | } OPKELE_RETHROW |
229 | } | 229 | } |
230 | signed_part_message_proxy signeds(om); | 230 | signed_part_message_proxy signeds(om); |
231 | if(o2) { | 231 | if(o2) { |
232 | check_nonce(om.get_field("op_endpoint"), | 232 | check_nonce(om.get_field("op_endpoint"), |
233 | om.get_field("response_nonce")); | 233 | om.get_field("response_nonce")); |
234 | static const char *mustsign[] = { | 234 | static const char *mustsign[] = { |
235 | "op_endpoint", "return_to", "response_nonce", "assoc_handle", | 235 | "op_endpoint", "return_to", "response_nonce", "assoc_handle", |
236 | "claimed_id", "identity" }; | 236 | "claimed_id", "identity" }; |
237 | for(int ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) { | 237 | for(int ms=0;ms<(sizeof(mustsign)/sizeof(*mustsign));++ms) { |
238 | if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms])) | 238 | if(om.has_field(mustsign[ms]) && !signeds.has_field(mustsign[ms])) |
239 | throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs"); | 239 | throw bad_input(OPKELE_CP_ string("Field '")+mustsign[ms]+"' is not signed against the specs"); |
240 | } | 240 | } |
241 | if( ( | 241 | if( ( |
242 | (om.has_field("claimed_id")?1:0) | 242 | (om.has_field("claimed_id")?1:0) |
243 | ^ | 243 | ^ |
244 | (om.has_field("identity")?1:0) | 244 | (om.has_field("identity")?1:0) |
245 | )&1 ) | 245 | )&1 ) |
246 | throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent"); | 246 | throw bad_input(OPKELE_CP_ "claimed_id and identity must be either both present or both absent"); |
247 | 247 | ||
248 | string turl = util::rfc_3986_normalize_uri(get_this_url()); | 248 | string turl = util::rfc_3986_normalize_uri(get_this_url()); |
249 | util::strip_uri_fragment_part(turl); | 249 | util::strip_uri_fragment_part(turl); |
250 | string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to")); | 250 | string rurl = util::rfc_3986_normalize_uri(om.get_field("return_to")); |
251 | util::strip_uri_fragment_part(rurl); | 251 | util::strip_uri_fragment_part(rurl); |
252 | string::size_type | 252 | string::size_type |
253 | tq = turl.find('?'), rq = rurl.find('?'); | 253 | tq = turl.find('?'), rq = rurl.find('?'); |
254 | if( | 254 | if( |
255 | ((tq==string::npos)?turl:turl.substr(0,tq)) | 255 | ((tq==string::npos)?turl:turl.substr(0,tq)) |
256 | != | 256 | != |
257 | ((rq==string::npos)?rurl:rurl.substr(0,rq)) | 257 | ((rq==string::npos)?rurl:rurl.substr(0,rq)) |
258 | ) | 258 | ) |
259 | throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url"); | 259 | throw id_res_bad_return_to(OPKELE_CP_ "return_to url doesn't match request url"); |
260 | map<string,string> tp; parse_query(turl,tq,tp); | 260 | map<string,string> tp; parse_query(turl,tq,tp); |
261 | map<string,string> rp; parse_query(rurl,rq,rp); | 261 | map<string,string> rp; parse_query(rurl,rq,rp); |
262 | for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) { | 262 | for(map<string,string>::const_iterator rpi=rp.begin();rpi!=rp.end();++rpi) { |
263 | map<string,string>::const_iterator tpi = tp.find(rpi->first); | 263 | map<string,string>::const_iterator tpi = tp.find(rpi->first); |
264 | if(tpi==tp.end()) | 264 | if(tpi==tp.end()) |
265 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request"); | 265 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to is missing from the request"); |
266 | if(tpi->second!=rpi->second) | 266 | if(tpi->second!=rpi->second) |
267 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request"); | 267 | throw id_res_bad_return_to(OPKELE_CP_ string("Parameter '")+rpi->first+"' from return_to doesn't matche the request"); |
268 | } | 268 | } |
269 | 269 | ||
270 | if(om.has_field("claimed_id")) { | 270 | if(om.has_field("claimed_id")) { |
271 | verify_OP( | 271 | verify_OP( |
272 | om.get_field("op_endpoint"), | 272 | om.get_field("op_endpoint"), |
273 | om.get_field("claimed_id"), | 273 | om.get_field("claimed_id"), |
274 | om.get_field("identity") ); | 274 | om.get_field("identity") ); |
275 | } | 275 | } |
276 | 276 | ||
277 | } | 277 | } |
278 | if(ext) ext->id_res_hook(om,signeds); | 278 | if(ext) ext->rp_id_res_hook(om,signeds); |
279 | } | 279 | } |
280 | 280 | ||
281 | void basic_RP::check_authentication(const string& OP, | 281 | void basic_RP::check_authentication(const string& OP, |
282 | const basic_openid_message& om){ | 282 | const basic_openid_message& om){ |
283 | openid_message_t res; | 283 | openid_message_t res; |
284 | static const string checkauthmode = "check_authentication"; | 284 | static const string checkauthmode = "check_authentication"; |
285 | direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP); | 285 | direct_request(res,util::change_mode_message_proxy(om,checkauthmode),OP); |
286 | if(res.has_field("is_valid")) { | 286 | if(res.has_field("is_valid")) { |
287 | if(res.get_field("is_valid")=="true") { | 287 | if(res.get_field("is_valid")=="true") { |
288 | if(res.has_field("invalidate_handle")) | 288 | if(res.has_field("invalidate_handle")) |
289 | invalidate_assoc(OP,res.get_field("invalidate_handle")); | 289 | invalidate_assoc(OP,res.get_field("invalidate_handle")); |
290 | return; | 290 | return; |
291 | } | 291 | } |
292 | } | 292 | } |
293 | throw failed_check_authentication( | 293 | throw failed_check_authentication( |
294 | OPKELE_CP_ "failed to verify response"); | 294 | OPKELE_CP_ "failed to verify response"); |
295 | } | 295 | } |
296 | 296 | ||
297 | } | 297 | } |
diff --git a/lib/extension.cc b/lib/extension.cc index 6451249..f7aaea5 100644 --- a/lib/extension.cc +++ b/lib/extension.cc | |||
@@ -1,15 +1,26 @@ | |||
1 | #include <opkele/exception.h> | 1 | #include <opkele/exception.h> |
2 | #include <opkele/extension.h> | 2 | #include <opkele/extension.h> |
3 | 3 | ||
4 | namespace opkele { | 4 | namespace opkele { |
5 | 5 | ||
6 | void extension_t::rp_checkid_hook(basic_openid_message&) { | ||
7 | throw not_implemented(OPKELE_CP_ "RP checkid_* hook not implemented"); } | ||
8 | void extension_t::rp_id_res_hook(const basic_openid_message&, | ||
9 | const basic_openid_message&) { | ||
10 | throw not_implemented(OPKELE_CP_ "RP id_res hook not implemented"); } | ||
11 | |||
12 | void extension_t::op_checkid_hook(const basic_openid_message&) { | ||
13 | throw not_implemented(OPKELE_CP_ "OP checkid_* hook not implemented"); } | ||
14 | void extension_t::op_id_res_hook(basic_openid_message& om) { | ||
15 | throw not_implemented(OPKELE_CP_ "OP id_res hook not implemented"); } | ||
16 | |||
17 | |||
6 | void extension_t::checkid_hook(basic_openid_message&) { | 18 | void extension_t::checkid_hook(basic_openid_message&) { |
7 | throw not_implemented(OPKELE_CP_ "Consumer checkid_hook not implemented"); | 19 | throw not_implemented(OPKELE_CP_ "deprecated consumer checkid_* hook not implemented"); } |
8 | } | 20 | void extension_t::id_res_hook(const basic_openid_message&, |
9 | void extension_t::id_res_hook(const basic_openid_message&,const basic_openid_message&) { | 21 | const basic_openid_message&) { |
10 | throw not_implemented(OPKELE_CP_ "Consumer id_res_hook not implemented"); | 22 | throw not_implemented(OPKELE_CP_ "deprecated consumer id_res hook not implemented"); } |
11 | } | 23 | |
12 | void extension_t::checkid_hook(const basic_openid_message&,basic_openid_message&) { | 24 | void extension_t::checkid_hook(const basic_openid_message&,basic_openid_message&) { |
13 | throw not_implemented(OPKELE_CP_ "Server checkid_hook not implemented"); | 25 | throw not_implemented(OPKELE_CP_ "deprecated server checkid hook not implemented"); } |
14 | } | ||
15 | } | 26 | } |
diff --git a/lib/extension_chain.cc b/lib/extension_chain.cc index 5c2afd9..5483740 100644 --- a/lib/extension_chain.cc +++ b/lib/extension_chain.cc | |||
@@ -1,16 +1,27 @@ | |||
1 | #include <cstdarg> | 1 | #include <cstdarg> |
2 | #include <opkele/extension_chain.h> | 2 | #include <opkele/extension_chain.h> |
3 | 3 | ||
4 | namespace opkele { | 4 | namespace opkele { |
5 | 5 | ||
6 | void extension_chain_t::rp_checkid_hook(basic_openid_message& om) { | ||
7 | for(iterator i=begin();i!=end();++i) (*i)->rp_checkid_hook(om); } | ||
8 | void extension_chain_t::rp_id_res_hook(const basic_openid_message& om, | ||
9 | const basic_openid_message& sp) { | ||
10 | for(iterator i=begin();i!=end();++i) (*i)->rp_id_res_hook(om,sp); } | ||
11 | |||
12 | void extension_chain_t::op_checkid_hook(const basic_openid_message& inm) { | ||
13 | for(iterator i=begin();i!=end();++i) (*i)->op_checkid_hook(inm); } | ||
14 | void extension_chain_t::op_id_res_hook(basic_openid_message& oum) { | ||
15 | for(iterator i=begin();i!=end();++i) (*i)->op_id_res_hook(oum); } | ||
16 | |||
17 | |||
6 | void extension_chain_t::checkid_hook(basic_openid_message& om){ | 18 | void extension_chain_t::checkid_hook(basic_openid_message& om){ |
7 | for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(om); | 19 | for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(om); } |
8 | } | 20 | void extension_chain_t::id_res_hook(const basic_openid_message& om, |
9 | void extension_chain_t::id_res_hook(const basic_openid_message& om,const basic_openid_message& sp) { | 21 | const basic_openid_message& sp) { |
10 | for(iterator i=begin();i!=end();++i) (*i)->id_res_hook(om,sp); | 22 | for(iterator i=begin();i!=end();++i) (*i)->id_res_hook(om,sp); } |
11 | } | 23 | void extension_chain_t::checkid_hook(const basic_openid_message& inm, |
12 | void extension_chain_t::checkid_hook(const basic_openid_message& inm,basic_openid_message& oum) { | 24 | basic_openid_message& oum) { |
13 | for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(inm,oum); | 25 | for(iterator i=begin();i!=end();++i) (*i)->checkid_hook(inm,oum); } |
14 | } | ||
15 | 26 | ||
16 | } | 27 | } |
diff --git a/lib/sreg.cc b/lib/sreg.cc index 7e2d588..b40cd45 100644 --- a/lib/sreg.cc +++ b/lib/sreg.cc | |||
@@ -1,140 +1,160 @@ | |||
1 | #include <opkele/exception.h> | 1 | #include <opkele/exception.h> |
2 | #include <opkele/sreg.h> | 2 | #include <opkele/sreg.h> |
3 | #include <opkele/uris.h> | 3 | #include <opkele/uris.h> |
4 | #include <algorithm> | 4 | #include <algorithm> |
5 | 5 | ||
6 | namespace opkele { | 6 | namespace opkele { |
7 | using std::find; | 7 | using std::find; |
8 | 8 | ||
9 | static const struct _sreg_field { | 9 | static const struct _sreg_field { |
10 | const char *fieldname; | 10 | const char *fieldname; |
11 | sreg_t::fieldbit_t fieldbit; | 11 | sreg_t::fieldbit_t fieldbit; |
12 | }fields[] = { | 12 | }fields[] = { |
13 | { "nickname", sreg_t::field_nickname }, | 13 | { "nickname", sreg_t::field_nickname }, |
14 | { "email", sreg_t::field_email }, | 14 | { "email", sreg_t::field_email }, |
15 | { "fullname", sreg_t::field_fullname }, | 15 | { "fullname", sreg_t::field_fullname }, |
16 | { "dob", sreg_t::field_dob }, | 16 | { "dob", sreg_t::field_dob }, |
17 | { "gender", sreg_t::field_gender }, | 17 | { "gender", sreg_t::field_gender }, |
18 | { "postcode", sreg_t::field_postcode }, | 18 | { "postcode", sreg_t::field_postcode }, |
19 | { "country", sreg_t::field_country }, | 19 | { "country", sreg_t::field_country }, |
20 | { "language", sreg_t::field_language }, | 20 | { "language", sreg_t::field_language }, |
21 | { "timezone", sreg_t::field_timezone } | 21 | { "timezone", sreg_t::field_timezone } |
22 | }; | 22 | }; |
23 | # define fields_BEGINfields | 23 | # define fields_BEGINfields |
24 | # define fields_END &fields[sizeof(fields)/sizeof(*fields)] | 24 | # define fields_END &fields[sizeof(fields)/sizeof(*fields)] |
25 | typedef const struct _sreg_field *fields_iterator; | 25 | typedef const struct _sreg_field *fields_iterator; |
26 | 26 | ||
27 | bool operator==(const struct _sreg_field& fd,const string& fn) { | 27 | bool operator==(const struct _sreg_field& fd,const string& fn) { |
28 | return fd.fieldname==fn; | 28 | return fd.fieldname==fn; |
29 | } | 29 | } |
30 | 30 | ||
31 | void sreg_t::checkid_hook(basic_openid_message& om) { | 31 | void sreg_t::rp_checkid_hook(basic_openid_message& om) { |
32 | string fr, fo; | 32 | string fr, fo; |
33 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { | 33 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { |
34 | if(f->fieldbit&fields_required) { | 34 | if(f->fieldbit&fields_required) { |
35 | if(!fr.empty()) fr+=","; | 35 | if(!fr.empty()) fr+=","; |
36 | fr += f->fieldname; | 36 | fr += f->fieldname; |
37 | } | 37 | } |
38 | if(f->fieldbit&fields_optional) { | 38 | if(f->fieldbit&fields_optional) { |
39 | if(!fo.empty()) fo+=","; | 39 | if(!fo.empty()) fo+=","; |
40 | fo += f->fieldname; | 40 | fo += f->fieldname; |
41 | } | 41 | } |
42 | } | 42 | } |
43 | string pfx = om.allocate_ns(OIURI_SREG11,"sreg"); | 43 | string pfx = om.allocate_ns(OIURI_SREG11,"sreg"); |
44 | if(!fr.empty()) om.set_field(pfx+".required",fr); | 44 | if(!fr.empty()) om.set_field(pfx+".required",fr); |
45 | if(!fo.empty()) om.set_field(pfx+".optional",fo); | 45 | if(!fo.empty()) om.set_field(pfx+".optional",fo); |
46 | if(!policy_url.empty()) om.set_field(pfx+".policy_url",policy_url); | 46 | if(!policy_url.empty()) om.set_field(pfx+".policy_url",policy_url); |
47 | } | 47 | } |
48 | 48 | ||
49 | void sreg_t::id_res_hook(const basic_openid_message& om,const basic_openid_message& sp) { | 49 | void sreg_t::checkid_hook(basic_openid_message& om) { |
50 | rp_checkid_hook(om); } | ||
51 | |||
52 | void sreg_t::rp_id_res_hook(const basic_openid_message& om, | ||
53 | const basic_openid_message& sp) { | ||
50 | clear(); | 54 | clear(); |
51 | string pfx; | 55 | string pfx; |
52 | try { | 56 | try { |
53 | pfx = om.find_ns(OIURI_SREG11,"sreg"); | 57 | pfx = om.find_ns(OIURI_SREG11,"sreg"); |
54 | }catch(failed_lookup& fl) { | 58 | }catch(failed_lookup& fl) { |
55 | try { | 59 | try { |
56 | pfx = om.find_ns(OIURI_SREG10,"sreg"); | 60 | pfx = om.find_ns(OIURI_SREG10,"sreg"); |
57 | }catch(failed_lookup& fl) { | 61 | }catch(failed_lookup& fl) { |
58 | return; | 62 | return; |
59 | } | 63 | } |
60 | } | 64 | } |
61 | pfx += '.'; | 65 | pfx += '.'; |
62 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { | 66 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { |
63 | string fn = pfx; fn+=f->fieldname; | 67 | string fn = pfx; fn+=f->fieldname; |
64 | if(!sp.has_field(fn)) continue; | 68 | if(!sp.has_field(fn)) continue; |
65 | has_fields |= f->fieldbit; | 69 | has_fields |= f->fieldbit; |
66 | response[f->fieldbit]=sp.get_field(fn); | 70 | response[f->fieldbit]=sp.get_field(fn); |
67 | } | 71 | } |
68 | } | 72 | } |
69 | 73 | ||
74 | void sreg_t::id_res_hook(const basic_openid_message& om, | ||
75 | const basic_openid_message& sp) { | ||
76 | rp_id_res_hook(om,sp); } | ||
77 | |||
70 | const string& sreg_t::get_field(fieldbit_t fb) const { | 78 | const string& sreg_t::get_field(fieldbit_t fb) const { |
71 | response_t::const_iterator i = response.find(fb); | 79 | response_t::const_iterator i = response.find(fb); |
72 | if(i==response.end()) | 80 | if(i==response.end()) |
73 | throw failed_lookup(OPKELE_CP_ "no field data available"); | 81 | throw failed_lookup(OPKELE_CP_ "no field data available"); |
74 | return i->second; | 82 | return i->second; |
75 | } | 83 | } |
76 | 84 | ||
77 | void sreg_t::set_field(fieldbit_t fb,const string& fv) { | 85 | void sreg_t::set_field(fieldbit_t fb,const string& fv) { |
78 | response[fb] = fv; | 86 | response[fb] = fv; |
79 | has_fields |= fb; | 87 | has_fields |= fb; |
80 | } | 88 | } |
81 | 89 | ||
82 | void sreg_t::reset_field(fieldbit_t fb) { | 90 | void sreg_t::reset_field(fieldbit_t fb) { |
83 | has_fields &= ~fb; | 91 | has_fields &= ~fb; |
84 | response.erase(fb); | 92 | response.erase(fb); |
85 | } | 93 | } |
86 | 94 | ||
87 | void sreg_t::clear() { | 95 | void sreg_t::clear() { |
88 | has_fields = 0; response.clear(); | 96 | has_fields = 0; response.clear(); |
89 | } | 97 | } |
90 | 98 | ||
91 | static long fields_list_to_bitmask(string& fl) { | 99 | static long fields_list_to_bitmask(string& fl) { |
92 | long rv = 0; | 100 | long rv = 0; |
93 | while(!fl.empty()) { | 101 | while(!fl.empty()) { |
94 | string::size_type co = fl.find(','); | 102 | string::size_type co = fl.find(','); |
95 | string fn; | 103 | string fn; |
96 | if(co==string::npos) { | 104 | if(co==string::npos) { |
97 | fn = fl; fl.erase(); | 105 | fn = fl; fl.erase(); |
98 | }else{ | 106 | }else{ |
99 | fn = fl.substr(0,co); fl.erase(0,co+1); | 107 | fn = fl.substr(0,co); fl.erase(0,co+1); |
100 | } | 108 | } |
101 | fields_iterator f = find(fields_BEGIN,fields_END,fn); | 109 | fields_iterator f = find(fields_BEGIN,fields_END,fn); |
102 | if(f!=fields_END) | 110 | if(f!=fields_END) |
103 | rv |= f->fieldbit; | 111 | rv |= f->fieldbit; |
104 | } | 112 | } |
105 | return rv; | 113 | return rv; |
106 | } | 114 | } |
107 | 115 | ||
108 | void sreg_t::checkid_hook(const basic_openid_message& inm,basic_openid_message& oum) { | 116 | void sreg_t::op_checkid_hook(const basic_openid_message& inm) { |
109 | string ins = inm.find_ns(OIURI_SREG11,"sreg"); | 117 | string ins = inm.find_ns(OIURI_SREG11,"sreg"); |
110 | fields_optional = 0; fields_required = 0; policy_url.erase(); | 118 | fields_optional = 0; fields_required = 0; policy_url.erase(); |
111 | fields_response = 0; | 119 | fields_response = 0; |
112 | try { | 120 | try { |
113 | string fl = inm.get_field(ins+".required"); | 121 | string fl = inm.get_field(ins+".required"); |
114 | fields_required = fields_list_to_bitmask(fl); | 122 | fields_required = fields_list_to_bitmask(fl); |
115 | }catch(failed_lookup&) { } | 123 | }catch(failed_lookup&) { } |
116 | try { | 124 | try { |
117 | string fl = inm.get_field(ins+".optional"); | 125 | string fl = inm.get_field(ins+".optional"); |
118 | fields_optional = fields_list_to_bitmask(fl); | 126 | fields_optional = fields_list_to_bitmask(fl); |
119 | }catch(failed_lookup&) { } | 127 | }catch(failed_lookup&) { } |
120 | try { | 128 | try { |
121 | policy_url = inm.get_field(ins+".policy_url"); | 129 | policy_url = inm.get_field(ins+".policy_url"); |
122 | }catch(failed_lookup&) { } | 130 | }catch(failed_lookup&) { } |
123 | setup_response(inm,oum); | 131 | } |
132 | |||
133 | void sreg_t::op_id_res_hook(basic_openid_message& oum) { | ||
124 | string ons = oum.allocate_ns(OIURI_SREG11,"sreg"); | 134 | string ons = oum.allocate_ns(OIURI_SREG11,"sreg"); |
125 | fields_response &= has_fields; | 135 | fields_response &= has_fields; |
126 | string signeds = "ns."+ons; | 136 | string signeds = "ns."+ons; |
127 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { | 137 | for(fields_iterator f=fields_BEGIN;f<fields_END;++f) { |
128 | if(!(f->fieldbit&fields_response)) continue; | 138 | if(!(f->fieldbit&fields_response)) continue; |
129 | signeds +=','; | 139 | signeds +=','; |
130 | string pn = ons; pn += '.'; pn += f->fieldname; | 140 | string pn = ons; pn += '.'; pn += f->fieldname; |
131 | signeds += pn; | 141 | signeds += pn; |
132 | oum.set_field(pn,get_field(f->fieldbit)); | 142 | oum.set_field(pn,get_field(f->fieldbit)); |
133 | } | 143 | } |
134 | oum.add_to_signed(signeds); | 144 | oum.add_to_signed(signeds); |
135 | } | 145 | } |
136 | 146 | ||
147 | void sreg_t::checkid_hook(const basic_openid_message& inm, | ||
148 | basic_openid_message& oum) { | ||
149 | op_checkid_hook(inm); | ||
150 | setup_response(inm,oum); | ||
151 | op_id_res_hook(oum); | ||
152 | } | ||
153 | |||
137 | void sreg_t::setup_response(const basic_openid_message& /* inm */,basic_openid_message& /* oum */) { | 154 | void sreg_t::setup_response(const basic_openid_message& /* inm */,basic_openid_message& /* oum */) { |
155 | setup_response(); | ||
156 | } | ||
157 | void sreg_t::setup_response() { | ||
138 | fields_response = (fields_required|fields_optional)&has_fields; | 158 | fields_response = (fields_required|fields_optional)&has_fields; |
139 | } | 159 | } |
140 | } | 160 | } |
@@ -1,409 +1,415 @@ | |||
1 | #include <uuid/uuid.h> | 1 | #include <uuid/uuid.h> |
2 | #include <iostream> | 2 | #include <iostream> |
3 | #include <cassert> | 3 | #include <cassert> |
4 | #include <string> | 4 | #include <string> |
5 | #include <ext/algorithm> | 5 | #include <ext/algorithm> |
6 | using namespace std; | 6 | using namespace std; |
7 | #include <kingate/exception.h> | 7 | #include <kingate/exception.h> |
8 | #include <kingate/plaincgi.h> | 8 | #include <kingate/plaincgi.h> |
9 | #include <kingate/cgi_gateway.h> | 9 | #include <kingate/cgi_gateway.h> |
10 | #include <opkele/exception.h> | 10 | #include <opkele/exception.h> |
11 | #include <opkele/util.h> | 11 | #include <opkele/util.h> |
12 | #include <opkele/uris.h> | 12 | #include <opkele/uris.h> |
13 | #include <opkele/extension.h> | 13 | #include <opkele/extension.h> |
14 | #include <opkele/association.h> | 14 | #include <opkele/association.h> |
15 | #include <opkele/debug.h> | 15 | #include <opkele/debug.h> |
16 | #include <opkele/verify_op.h> | 16 | #include <opkele/verify_op.h> |
17 | #include <opkele/sreg.h> | ||
17 | 18 | ||
18 | #include "sqlite.h" | 19 | #include "sqlite.h" |
19 | #include "kingate_openid_message.h" | 20 | #include "kingate_openid_message.h" |
20 | 21 | ||
21 | static const string get_self_url(const kingate::cgi_gateway& gw) { | 22 | static const string get_self_url(const kingate::cgi_gateway& gw) { |
22 | bool s = gw.has_meta("SSL_PROTOCOL_VERSION"); | 23 | bool s = gw.has_meta("SSL_PROTOCOL_VERSION"); |
23 | string rv = s?"https://":"http://"; | 24 | string rv = s?"https://":"http://"; |
24 | rv += gw.http_request_header("Host"); | 25 | rv += gw.http_request_header("Host"); |
25 | const string& port = gw.get_meta("SERVER_PORT"); | 26 | const string& port = gw.get_meta("SERVER_PORT"); |
26 | if( port!=(s?"443":"80") ) { | 27 | if( port!=(s?"443":"80") ) { |
27 | rv += ':'; rv += port; | 28 | rv += ':'; rv += port; |
28 | } | 29 | } |
29 | rv += gw.get_meta("REQUEST_URI"); | 30 | rv += gw.get_meta("REQUEST_URI"); |
30 | string::size_type q = rv.find('?'); | 31 | string::size_type q = rv.find('?'); |
31 | if(q!=string::npos) | 32 | if(q!=string::npos) |
32 | rv.erase(q); | 33 | rv.erase(q); |
33 | return rv; | 34 | return rv; |
34 | } | 35 | } |
35 | 36 | ||
36 | class opdb_t : public sqlite3_t { | 37 | class opdb_t : public sqlite3_t { |
37 | public: | 38 | public: |
38 | opdb_t() | 39 | opdb_t() |
39 | : sqlite3_t("/tmp/OP.db") { | 40 | : sqlite3_t("/tmp/OP.db") { |
40 | assert(_D); | 41 | assert(_D); |
41 | char **resp; int nr,nc; char *errm; | 42 | char **resp; int nr,nc; char *errm; |
42 | if(sqlite3_get_table( | 43 | if(sqlite3_get_table( |
43 | _D, "SELECT a_op FROM assoc LIMIT 0", | 44 | _D, "SELECT a_op FROM assoc LIMIT 0", |
44 | &resp,&nr,&nc,&errm)!=SQLITE_OK) { | 45 | &resp,&nr,&nc,&errm)!=SQLITE_OK) { |
45 | extern const char *__OP_db_bootstrap; | 46 | extern const char *__OP_db_bootstrap; |
46 | DOUT_("Bootstrapping DB"); | 47 | DOUT_("Bootstrapping DB"); |
47 | if(sqlite3_exec(_D,__OP_db_bootstrap,NULL,NULL,&errm)!=SQLITE_OK) | 48 | if(sqlite3_exec(_D,__OP_db_bootstrap,NULL,NULL,&errm)!=SQLITE_OK) |
48 | throw opkele::exception(OPKELE_CP_ string("Failed to boostrap SQLite database: ")+errm); | 49 | throw opkele::exception(OPKELE_CP_ string("Failed to boostrap SQLite database: ")+errm); |
49 | }else | 50 | }else |
50 | sqlite3_free_table(resp); | 51 | sqlite3_free_table(resp); |
51 | } | 52 | } |
52 | }; | 53 | }; |
53 | 54 | ||
54 | class example_op_t : public opkele::verify_op { | 55 | class example_op_t : public opkele::verify_op { |
55 | public: | 56 | public: |
56 | kingate::cgi_gateway& gw; | 57 | kingate::cgi_gateway& gw; |
57 | opdb_t db; | 58 | opdb_t db; |
58 | kingate::cookie htc; | 59 | kingate::cookie htc; |
59 | 60 | ||
60 | 61 | ||
61 | example_op_t(kingate::cgi_gateway& gw) | 62 | example_op_t(kingate::cgi_gateway& gw) |
62 | : gw(gw) { | 63 | : gw(gw) { |
63 | try { | 64 | try { |
64 | htc = gw.cookies.get_cookie("htop_session"); | 65 | htc = gw.cookies.get_cookie("htop_session"); |
65 | sqlite3_mem_t<char*> S = sqlite3_mprintf( | 66 | sqlite3_mem_t<char*> S = sqlite3_mprintf( |
66 | "SELECT 1 FROM ht_sessions WHERE hts_id=%Q", | 67 | "SELECT 1 FROM ht_sessions WHERE hts_id=%Q", |
67 | htc.get_value().c_str()); | 68 | htc.get_value().c_str()); |
68 | sqlite3_table_t T; int nr,nc; | 69 | sqlite3_table_t T; int nr,nc; |
69 | db.get_table(S,T,&nr,&nc); | 70 | db.get_table(S,T,&nr,&nc); |
70 | if(nr<1) | 71 | if(nr<1) |
71 | throw kingate::exception_notfound(CODEPOINT,"forcing cookie generation"); | 72 | throw kingate::exception_notfound(CODEPOINT,"forcing cookie generation"); |
72 | }catch(kingate::exception_notfound& kenf) { | 73 | }catch(kingate::exception_notfound& kenf) { |
73 | uuid_t uuid; uuid_generate(uuid); | 74 | uuid_t uuid; uuid_generate(uuid); |
74 | htc = kingate::cookie("htop_session",opkele::util::encode_base64(uuid,sizeof(uuid))); | 75 | htc = kingate::cookie("htop_session",opkele::util::encode_base64(uuid,sizeof(uuid))); |
75 | sqlite3_mem_t<char*> S = sqlite3_mprintf( | 76 | sqlite3_mem_t<char*> S = sqlite3_mprintf( |
76 | "INSERT INTO ht_sessions (hts_id) VALUES (%Q)", | 77 | "INSERT INTO ht_sessions (hts_id) VALUES (%Q)", |
77 | htc.get_value().c_str()); | 78 | htc.get_value().c_str()); |
78 | db.exec(S); | 79 | db.exec(S); |
79 | } | 80 | } |
80 | } | 81 | } |
81 | 82 | ||
82 | void set_authorized(bool a) { | 83 | void set_authorized(bool a) { |
83 | sqlite3_mem_t<char*> | 84 | sqlite3_mem_t<char*> |
84 | S = sqlite3_mprintf( | 85 | S = sqlite3_mprintf( |
85 | "UPDATE ht_sessions" | 86 | "UPDATE ht_sessions" |
86 | " SET authorized=%d" | 87 | " SET authorized=%d" |
87 | " WHERE hts_id=%Q", | 88 | " WHERE hts_id=%Q", |
88 | (int)a,htc.get_value().c_str()); | 89 | (int)a,htc.get_value().c_str()); |
89 | db.exec(S); | 90 | db.exec(S); |
90 | } | 91 | } |
91 | bool get_authorized() { | 92 | bool get_authorized() { |
92 | sqlite3_mem_t<char*> | 93 | sqlite3_mem_t<char*> |
93 | S = sqlite3_mprintf( | 94 | S = sqlite3_mprintf( |
94 | "SELECT authorized" | 95 | "SELECT authorized" |
95 | " FROM ht_sessions" | 96 | " FROM ht_sessions" |
96 | " WHERE hts_id=%Q", | 97 | " WHERE hts_id=%Q", |
97 | htc.get_value().c_str()); | 98 | htc.get_value().c_str()); |
98 | sqlite3_table_t T; int nr,nc; | 99 | sqlite3_table_t T; int nr,nc; |
99 | db.get_table(S,T,&nr,&nc); | 100 | db.get_table(S,T,&nr,&nc); |
100 | assert(nr==1); assert(nc=1); | 101 | assert(nr==1); assert(nc=1); |
101 | return opkele::util::string_to_long(T.get(1,0,nc)); | 102 | return opkele::util::string_to_long(T.get(1,0,nc)); |
102 | } | 103 | } |
103 | 104 | ||
104 | ostream& cookie_header(ostream& o) const { | 105 | ostream& cookie_header(ostream& o) const { |
105 | o << "Set-Cookie: " << htc.set_cookie_header() << "\n"; | 106 | o << "Set-Cookie: " << htc.set_cookie_header() << "\n"; |
106 | return o; | 107 | return o; |
107 | } | 108 | } |
108 | 109 | ||
109 | opkele::assoc_t alloc_assoc(const string& type,size_t klength,bool sl) { | 110 | opkele::assoc_t alloc_assoc(const string& type,size_t klength,bool sl) { |
110 | uuid_t uuid; uuid_generate(uuid); | 111 | uuid_t uuid; uuid_generate(uuid); |
111 | string a_handle = opkele::util::encode_base64(uuid,sizeof(uuid)); | 112 | string a_handle = opkele::util::encode_base64(uuid,sizeof(uuid)); |
112 | opkele::secret_t a_secret; | 113 | opkele::secret_t a_secret; |
113 | generate_n( | 114 | generate_n( |
114 | back_insert_iterator<opkele::secret_t>(a_secret),klength, | 115 | back_insert_iterator<opkele::secret_t>(a_secret),klength, |
115 | rand ); | 116 | rand ); |
116 | string ssecret; a_secret.to_base64(ssecret); | 117 | string ssecret; a_secret.to_base64(ssecret); |
117 | time_t now = time(0); | 118 | time_t now = time(0); |
118 | int expires_in = sl?3600*2:3600*24*7*2; | 119 | int expires_in = sl?3600*2:3600*24*7*2; |
119 | sqlite3_mem_t<char*> | 120 | sqlite3_mem_t<char*> |
120 | S = sqlite3_mprintf( | 121 | S = sqlite3_mprintf( |
121 | "INSERT INTO assoc" | 122 | "INSERT INTO assoc" |
122 | " (a_handle,a_type,a_ctime,a_etime,a_secret,a_stateless)" | 123 | " (a_handle,a_type,a_ctime,a_etime,a_secret,a_stateless)" |
123 | " VALUES (" | 124 | " VALUES (" |
124 | " %Q,%Q,datetime('now')," | 125 | " %Q,%Q,datetime('now')," |
125 | " datetime('now','+%d seconds')," | 126 | " datetime('now','+%d seconds')," |
126 | " %Q,%d );", | 127 | " %Q,%d );", |
127 | a_handle.c_str(), type.c_str(), | 128 | a_handle.c_str(), type.c_str(), |
128 | expires_in, | 129 | expires_in, |
129 | ssecret.c_str(), sl ); | 130 | ssecret.c_str(), sl ); |
130 | db.exec(S); | 131 | db.exec(S); |
131 | return opkele::assoc_t(new opkele::association( | 132 | return opkele::assoc_t(new opkele::association( |
132 | "", | 133 | "", |
133 | a_handle, type, a_secret, | 134 | a_handle, type, a_secret, |
134 | now+expires_in, sl )); | 135 | now+expires_in, sl )); |
135 | } | 136 | } |
136 | 137 | ||
137 | opkele::assoc_t retrieve_assoc(const string& h) { | 138 | opkele::assoc_t retrieve_assoc(const string& h) { |
138 | sqlite3_mem_t<char*> | 139 | sqlite3_mem_t<char*> |
139 | S = sqlite3_mprintf( | 140 | S = sqlite3_mprintf( |
140 | "SELECT" | 141 | "SELECT" |
141 | " a_handle,a_type,a_secret,a_stateless," | 142 | " a_handle,a_type,a_secret,a_stateless," |
142 | " strftime('%%s',a_etime) AS a_etime," | 143 | " strftime('%%s',a_etime) AS a_etime," |
143 | " a_itime" | 144 | " a_itime" |
144 | " FROM assoc" | 145 | " FROM assoc" |
145 | " WHERE a_handle=%Q AND a_itime IS NULL" | 146 | " WHERE a_handle=%Q AND a_itime IS NULL" |
146 | " AND datetime('now') < a_etime" | 147 | " AND datetime('now') < a_etime" |
147 | " LIMIT 1", | 148 | " LIMIT 1", |
148 | h.c_str() ); | 149 | h.c_str() ); |
149 | sqlite3_table_t T; | 150 | sqlite3_table_t T; |
150 | int nr,nc; | 151 | int nr,nc; |
151 | db.get_table(S,T,&nr,&nc); | 152 | db.get_table(S,T,&nr,&nc); |
152 | if(nr<1) | 153 | if(nr<1) |
153 | throw opkele::failed_lookup(OPKELE_CP_ | 154 | throw opkele::failed_lookup(OPKELE_CP_ |
154 | "couldn't retrieve valid unexpired assoc"); | 155 | "couldn't retrieve valid unexpired assoc"); |
155 | assert(nr==1); assert(nc==6); | 156 | assert(nr==1); assert(nc==6); |
156 | opkele::secret_t secret; opkele::util::decode_base64(T.get(1,2,nc),secret); | 157 | opkele::secret_t secret; opkele::util::decode_base64(T.get(1,2,nc),secret); |
157 | return opkele::assoc_t(new opkele::association( | 158 | return opkele::assoc_t(new opkele::association( |
158 | "", h, T.get(1,1,nc), secret, | 159 | "", h, T.get(1,1,nc), secret, |
159 | strtol(T.get(1,4,nc),0,0), | 160 | strtol(T.get(1,4,nc),0,0), |
160 | strtol(T.get(1,3,nc),0,0) )); | 161 | strtol(T.get(1,3,nc),0,0) )); |
161 | } | 162 | } |
162 | 163 | ||
163 | string& alloc_nonce(string& nonce,bool stateless) { | 164 | string& alloc_nonce(string& nonce,bool stateless) { |
164 | uuid_t uuid; uuid_generate(uuid); | 165 | uuid_t uuid; uuid_generate(uuid); |
165 | nonce += opkele::util::encode_base64(uuid,sizeof(uuid)); | 166 | nonce += opkele::util::encode_base64(uuid,sizeof(uuid)); |
166 | sqlite3_mem_t<char*> | 167 | sqlite3_mem_t<char*> |
167 | S = sqlite3_mprintf( | 168 | S = sqlite3_mprintf( |
168 | "INSERT INTO nonces" | 169 | "INSERT INTO nonces" |
169 | " (n_once) VALUES (%Q)", | 170 | " (n_once) VALUES (%Q)", |
170 | nonce.c_str() ); | 171 | nonce.c_str() ); |
171 | db.exec(S); | 172 | db.exec(S); |
172 | return nonce; | 173 | return nonce; |
173 | } | 174 | } |
174 | bool check_nonce(const string& nonce) { | 175 | bool check_nonce(const string& nonce) { |
175 | sqlite3_mem_t<char*> | 176 | sqlite3_mem_t<char*> |
176 | S = sqlite3_mprintf( | 177 | S = sqlite3_mprintf( |
177 | "SELECT 1" | 178 | "SELECT 1" |
178 | " FROM nonces" | 179 | " FROM nonces" |
179 | " WHERE n_once=%Q AND n_itime IS NULL", | 180 | " WHERE n_once=%Q AND n_itime IS NULL", |
180 | nonce.c_str()); | 181 | nonce.c_str()); |
181 | sqlite3_table_t T; | 182 | sqlite3_table_t T; |
182 | int nr,nc; | 183 | int nr,nc; |
183 | db.get_table(S,T,&nr,&nc); | 184 | db.get_table(S,T,&nr,&nc); |
184 | return nr>=1; | 185 | return nr>=1; |
185 | } | 186 | } |
186 | void invalidate_nonce(const string& nonce) { | 187 | void invalidate_nonce(const string& nonce) { |
187 | sqlite3_mem_t<char*> | 188 | sqlite3_mem_t<char*> |
188 | S = sqlite3_mprintf( | 189 | S = sqlite3_mprintf( |
189 | "UPDATE nonces" | 190 | "UPDATE nonces" |
190 | " SET n_itime=datetime('now')" | 191 | " SET n_itime=datetime('now')" |
191 | " WHERE n_once=%Q", | 192 | " WHERE n_once=%Q", |
192 | nonce.c_str()); | 193 | nonce.c_str()); |
193 | db.exec(S); | 194 | db.exec(S); |
194 | } | 195 | } |
195 | 196 | ||
196 | const string get_op_endpoint() const { | 197 | const string get_op_endpoint() const { |
197 | return get_self_url(gw); | 198 | return get_self_url(gw); |
198 | } | 199 | } |
199 | 200 | ||
200 | }; | 201 | }; |
201 | 202 | ||
202 | int main(int argc,char *argv[]) { | 203 | int main(int argc,char *argv[]) { |
203 | try { | 204 | try { |
204 | kingate::plaincgi_interface ci; | 205 | kingate::plaincgi_interface ci; |
205 | kingate::cgi_gateway gw(ci); | 206 | kingate::cgi_gateway gw(ci); |
206 | string op; | 207 | string op; |
207 | try { op = gw.get_param("op"); }catch(kingate::exception_notfound&) { } | 208 | try { op = gw.get_param("op"); }catch(kingate::exception_notfound&) { } |
208 | string message; | 209 | string message; |
209 | if(op=="set_password") { | 210 | if(op=="set_password") { |
210 | example_op_t OP(gw); | 211 | example_op_t OP(gw); |
211 | string password = gw.get_param("password"); | 212 | string password = gw.get_param("password"); |
212 | sqlite3_mem_t<char*> | 213 | sqlite3_mem_t<char*> |
213 | Sget = sqlite3_mprintf("SELECT s_password FROM setup LIMIT 1"); | 214 | Sget = sqlite3_mprintf("SELECT s_password FROM setup LIMIT 1"); |
214 | sqlite3_table_t T; int nr,nc; | 215 | sqlite3_table_t T; int nr,nc; |
215 | OP.db.get_table(Sget,T,&nr,&nc); | 216 | OP.db.get_table(Sget,T,&nr,&nc); |
216 | if(nr>=1) | 217 | if(nr>=1) |
217 | throw opkele::exception(OPKELE_CP_ "Password already set"); | 218 | throw opkele::exception(OPKELE_CP_ "Password already set"); |
218 | sqlite3_mem_t<char*> | 219 | sqlite3_mem_t<char*> |
219 | Sset = sqlite3_mprintf( | 220 | Sset = sqlite3_mprintf( |
220 | "INSERT INTO setup (s_password) VALUES (%Q)", | 221 | "INSERT INTO setup (s_password) VALUES (%Q)", |
221 | password.c_str()); | 222 | password.c_str()); |
222 | OP.db.exec(Sset); | 223 | OP.db.exec(Sset); |
223 | op.clear(); | 224 | op.clear(); |
224 | message = "password set"; | 225 | message = "password set"; |
225 | }else if(op=="login") { | 226 | }else if(op=="login") { |
226 | example_op_t OP(gw); | 227 | example_op_t OP(gw); |
227 | string password = gw.get_param("password"); | 228 | string password = gw.get_param("password"); |
228 | sqlite3_mem_t<char*> | 229 | sqlite3_mem_t<char*> |
229 | Sget = sqlite3_mprintf("SELECT s_password FROM setup LIMIT 1"); | 230 | Sget = sqlite3_mprintf("SELECT s_password FROM setup LIMIT 1"); |
230 | sqlite3_table_t T; int nr,nc; | 231 | sqlite3_table_t T; int nr,nc; |
231 | OP.db.get_table(Sget,T,&nr,&nc); | 232 | OP.db.get_table(Sget,T,&nr,&nc); |
232 | if(nr<1) | 233 | if(nr<1) |
233 | throw opkele::exception(OPKELE_CP_ "no password set"); | 234 | throw opkele::exception(OPKELE_CP_ "no password set"); |
234 | if(password!=T.get(1,0,nc)) | 235 | if(password!=T.get(1,0,nc)) |
235 | throw opkele::exception(OPKELE_CP_ "wrong password"); | 236 | throw opkele::exception(OPKELE_CP_ "wrong password"); |
236 | OP.set_authorized(true); | 237 | OP.set_authorized(true); |
237 | op.clear(); | 238 | op.clear(); |
238 | message = "logged in"; | 239 | message = "logged in"; |
239 | OP.cookie_header(cout); | 240 | OP.cookie_header(cout); |
240 | }else if(op=="logout") { | 241 | }else if(op=="logout") { |
241 | example_op_t OP(gw); | 242 | example_op_t OP(gw); |
242 | OP.set_authorized(false); | 243 | OP.set_authorized(false); |
243 | op.clear(); | 244 | op.clear(); |
244 | message = "logged out"; | 245 | message = "logged out"; |
245 | } | 246 | } |
246 | string om; | 247 | string om; |
247 | try { om = gw.get_param("openid.mode"); }catch(kingate::exception_notfound&) { } | 248 | try { om = gw.get_param("openid.mode"); }catch(kingate::exception_notfound&) { } |
248 | if(op=="xrds") { | 249 | if(op=="xrds") { |
249 | cout << | 250 | cout << |
250 | "Content-type: application/xrds+xml\n\n" | 251 | "Content-type: application/xrds+xml\n\n" |
251 | "<?xml version='1.0' encoding='utf-8'?>" | 252 | "<?xml version='1.0' encoding='utf-8'?>" |
252 | "<xrds:XRDS xmlns:xrds='xri://$xrds' xmlns='xri://$xrd*($v*2.0)'>" | 253 | "<xrds:XRDS xmlns:xrds='xri://$xrds' xmlns='xri://$xrd*($v*2.0)'>" |
253 | "<XRD>" | 254 | "<XRD>" |
254 | "<Service>" | 255 | "<Service>" |
255 | "<Type>" STURI_OPENID20 "</Type>" | 256 | "<Type>" STURI_OPENID20 "</Type>" |
256 | "<URI>" << get_self_url(gw) << "</URI>" | 257 | "<URI>" << get_self_url(gw) << "</URI>" |
257 | "</Service>"; | 258 | "</Service>"; |
258 | if(gw.has_param("idsel")){ | 259 | if(gw.has_param("idsel")){ |
259 | cout << | 260 | cout << |
260 | "<Service>" | 261 | "<Service>" |
261 | "<Type>" STURI_OPENID20_OP "</Type>" | 262 | "<Type>" STURI_OPENID20_OP "</Type>" |
262 | "<URI>" << get_self_url(gw) << "</URI>"; | 263 | "<URI>" << get_self_url(gw) << "</URI>"; |
263 | } | 264 | } |
264 | cout << | 265 | cout << |
265 | "</XRD>" | 266 | "</XRD>" |
266 | "</xrds:XRDS>"; | 267 | "</xrds:XRDS>"; |
267 | }else if(op=="id_res" || op=="cancel") { | 268 | }else if(op=="id_res" || op=="cancel") { |
268 | kingate_openid_message_t inm(gw); | 269 | kingate_openid_message_t inm(gw); |
269 | example_op_t OP(gw); | 270 | example_op_t OP(gw); |
270 | if(gw.get_param("hts_id")!=OP.htc.get_value()) | 271 | if(gw.get_param("hts_id")!=OP.htc.get_value()) |
271 | throw opkele::exception(OPKELE_CP_ "toying around, huh?"); | 272 | throw opkele::exception(OPKELE_CP_ "toying around, huh?"); |
272 | OP.checkid_(inm,0); | 273 | opkele::sreg_t sreg; |
274 | OP.checkid_(inm,sreg); | ||
273 | OP.cookie_header(cout); | 275 | OP.cookie_header(cout); |
274 | opkele::openid_message_t om; | 276 | opkele::openid_message_t om; |
275 | if(op=="id_res") { | 277 | if(op=="id_res") { |
276 | if(!OP.get_authorized()) | 278 | if(!OP.get_authorized()) |
277 | throw opkele::exception(OPKELE_CP_ "not logged in"); | 279 | throw opkele::exception(OPKELE_CP_ "not logged in"); |
278 | if(OP.is_id_select()) { | 280 | if(OP.is_id_select()) { |
279 | OP.select_identity( get_self_url(gw), get_self_url(gw) ); | 281 | OP.select_identity( get_self_url(gw), get_self_url(gw) ); |
280 | } | 282 | } |
283 | sreg.set_field(opkele::sreg_t::field_nickname,"anonymous"); | ||
284 | sreg.set_field(opkele::sreg_t::field_fullname,"Ann O'Nymus"); | ||
285 | sreg.set_field(opkele::sreg_t::field_gender,"F"); | ||
286 | sreg.setup_response(); | ||
281 | cout << | 287 | cout << |
282 | "Status: 302 Going back to RP with id_res\n" | 288 | "Status: 302 Going back to RP with id_res\n" |
283 | "Location: " << OP.id_res(om).append_query(OP.get_return_to()) | 289 | "Location: " << OP.id_res(om,sreg).append_query(OP.get_return_to()) |
284 | << "\n\n"; | 290 | << "\n\n"; |
285 | }else{ | 291 | }else{ |
286 | cout << | 292 | cout << |
287 | "Status: 302 Going back to RP with cancel\n" | 293 | "Status: 302 Going back to RP with cancel\n" |
288 | "Location: " << OP.cancel(om).append_query(OP.get_return_to()) | 294 | "Location: " << OP.cancel(om).append_query(OP.get_return_to()) |
289 | << "\n\n"; | 295 | << "\n\n"; |
290 | } | 296 | } |
291 | om.to_keyvalues(clog); | 297 | om.to_keyvalues(clog); |
292 | }else if(om=="associate") { | 298 | }else if(om=="associate") { |
293 | kingate_openid_message_t inm(gw); | 299 | kingate_openid_message_t inm(gw); |
294 | opkele::openid_message_t oum; | 300 | opkele::openid_message_t oum; |
295 | example_op_t OP(gw); | 301 | example_op_t OP(gw); |
296 | OP.associate(oum,inm); | 302 | OP.associate(oum,inm); |
297 | cout << "Content-type: text/plain\n\n"; | 303 | cout << "Content-type: text/plain\n\n"; |
298 | oum.to_keyvalues(cout); | 304 | oum.to_keyvalues(cout); |
299 | }else if(om=="checkid_setup") { | 305 | }else if(om=="checkid_setup") { |
300 | kingate_openid_message_t inm(gw); | 306 | kingate_openid_message_t inm(gw); |
301 | example_op_t OP(gw); | 307 | example_op_t OP(gw); |
302 | OP.checkid_(inm,0); | 308 | OP.checkid_(inm,0); |
303 | OP.cookie_header(cout) << | 309 | OP.cookie_header(cout) << |
304 | "Content-type: text/html\n" | 310 | "Content-type: text/html\n" |
305 | "\n" | 311 | "\n" |
306 | 312 | ||
307 | "<html>" | 313 | "<html>" |
308 | "<head>" | 314 | "<head>" |
309 | "<title>test OP: confirm authentication</title>" | 315 | "<title>test OP: confirm authentication</title>" |
310 | "</head>" | 316 | "</head>" |
311 | "<body>" | 317 | "<body>" |
312 | "realm: " << OP.get_realm() << "<br/>" | 318 | "realm: " << OP.get_realm() << "<br/>" |
313 | "return_to: " << OP.get_return_to() << "<br/>" | 319 | "return_to: " << OP.get_return_to() << "<br/>" |
314 | "claimed_id: " << OP.get_claimed_id() << "<br/>" | 320 | "claimed_id: " << OP.get_claimed_id() << "<br/>" |
315 | "identity: " << OP.get_identity() << "<br/>"; | 321 | "identity: " << OP.get_identity() << "<br/>"; |
316 | if(OP.is_id_select()) { | 322 | if(OP.is_id_select()) { |
317 | OP.select_identity( get_self_url(gw), get_self_url(gw) ); | 323 | OP.select_identity( get_self_url(gw), get_self_url(gw) ); |
318 | cout << | 324 | cout << |
319 | "selected claimed_id: " << OP.get_claimed_id() << "<br/>" | 325 | "selected claimed_id: " << OP.get_claimed_id() << "<br/>" |
320 | "selected identity: " << OP.get_identity() << "<br/>"; | 326 | "selected identity: " << OP.get_identity() << "<br/>"; |
321 | } | 327 | } |
322 | cout << | 328 | cout << |
323 | "<form method='post'>"; | 329 | "<form method='post'>"; |
324 | inm.to_htmlhiddens(cout); | 330 | inm.to_htmlhiddens(cout); |
325 | cout << | 331 | cout << |
326 | "<input type='hidden' name='hts_id'" | 332 | "<input type='hidden' name='hts_id'" |
327 | " value='" << opkele::util::attr_escape(OP.htc.get_value()) << "'/>" | 333 | " value='" << opkele::util::attr_escape(OP.htc.get_value()) << "'/>" |
328 | "<input type='submit' name='op' value='id_res'/>" | 334 | "<input type='submit' name='op' value='id_res'/>" |
329 | "<input type='submit' name='op' value='cancel'/>" | 335 | "<input type='submit' name='op' value='cancel'/>" |
330 | "</form>" | 336 | "</form>" |
331 | "</body>" | 337 | "</body>" |
332 | "</html>"; | 338 | "</html>"; |
333 | }else if(om=="check_authentication") { | 339 | }else if(om=="check_authentication") { |
334 | kingate_openid_message_t inm(gw); | 340 | kingate_openid_message_t inm(gw); |
335 | example_op_t OP(gw); | 341 | example_op_t OP(gw); |
336 | opkele::openid_message_t oum; | 342 | opkele::openid_message_t oum; |
337 | OP.check_authentication(oum,inm); | 343 | OP.check_authentication(oum,inm); |
338 | cout << "Content-type: text/plain\n\n"; | 344 | cout << "Content-type: text/plain\n\n"; |
339 | oum.to_keyvalues(cout); | 345 | oum.to_keyvalues(cout); |
340 | oum.to_keyvalues(clog); | 346 | oum.to_keyvalues(clog); |
341 | }else{ | 347 | }else{ |
342 | example_op_t OP(gw); | 348 | example_op_t OP(gw); |
343 | string idsel; | 349 | string idsel; |
344 | if(gw.has_param("idsel")) | 350 | if(gw.has_param("idsel")) |
345 | idsel = "&idsel=idsel"; | 351 | idsel = "&idsel=idsel"; |
346 | OP.cookie_header(cout) << | 352 | OP.cookie_header(cout) << |
347 | "Content-type: text/html\n" | 353 | "Content-type: text/html\n" |
348 | "X-XRDS-Location: " << get_self_url(gw) << "?op=xrds" << idsel << "\n" | 354 | "X-XRDS-Location: " << get_self_url(gw) << "?op=xrds" << idsel << "\n" |
349 | "\n" | 355 | "\n" |
350 | 356 | ||
351 | "<html>" | 357 | "<html>" |
352 | "<head>" | 358 | "<head>" |
353 | "<title>test OP</title>" | 359 | "<title>test OP</title>" |
354 | "<link rel='openid.server' href='" << get_self_url(gw) << "'/>" | 360 | "<link rel='openid.server' href='" << get_self_url(gw) << "'/>" |
355 | "</head>" | 361 | "</head>" |
356 | "<body>" | 362 | "<body>" |
357 | "test openid 2.0 endpoint" | 363 | "test openid 2.0 endpoint" |
358 | "<br/>" | 364 | "<br/>" |
359 | "<a href='" << get_self_url(gw) << "?op=xrds" << idsel << "'>XRDS document</a>" | 365 | "<a href='" << get_self_url(gw) << "?op=xrds" << idsel << "'>XRDS document</a>" |
360 | "<br/>" | 366 | "<br/>" |
361 | "<h1>" << message << "</h1>"; | 367 | "<h1>" << message << "</h1>"; |
362 | sqlite3_mem_t<char*> | 368 | sqlite3_mem_t<char*> |
363 | S = sqlite3_mprintf("SELECT s_password FROM setup LIMIT 1"); | 369 | S = sqlite3_mprintf("SELECT s_password FROM setup LIMIT 1"); |
364 | sqlite3_table_t T; int nr,nc; | 370 | sqlite3_table_t T; int nr,nc; |
365 | OP.db.get_table(S,T,&nr,&nc); | 371 | OP.db.get_table(S,T,&nr,&nc); |
366 | if(nr<1) { | 372 | if(nr<1) { |
367 | cout << | 373 | cout << |
368 | "<form method='post'>" | 374 | "<form method='post'>" |
369 | "set password " | 375 | "set password " |
370 | "<input type='hidden' name='op' value='set_password'/>" | 376 | "<input type='hidden' name='op' value='set_password'/>" |
371 | "<input type='password' name='password' value=''/>" | 377 | "<input type='password' name='password' value=''/>" |
372 | "<input type='submit' name='submit' value='submit'/>" | 378 | "<input type='submit' name='submit' value='submit'/>" |
373 | "</form>"; | 379 | "</form>"; |
374 | }else if(OP.get_authorized()) { | 380 | }else if(OP.get_authorized()) { |
375 | cout << | 381 | cout << |
376 | "<br/>" | 382 | "<br/>" |
377 | "<a href='" << get_self_url(gw) << "?op=logout'>logout</a>"; | 383 | "<a href='" << get_self_url(gw) << "?op=logout'>logout</a>"; |
378 | }else{ | 384 | }else{ |
379 | cout << | 385 | cout << |
380 | "<form method='post'>" | 386 | "<form method='post'>" |
381 | "login " | 387 | "login " |
382 | "<input type='hidden' name='op' value='login'/>" | 388 | "<input type='hidden' name='op' value='login'/>" |
383 | "<input type='password' name='password' value=''/>" | 389 | "<input type='password' name='password' value=''/>" |
384 | "<input type='submit' name='submit' value='submit'/>" | 390 | "<input type='submit' name='submit' value='submit'/>" |
385 | "</form>"; | 391 | "</form>"; |
386 | } | 392 | } |
387 | cout << "</body>"; | 393 | cout << "</body>"; |
388 | } | 394 | } |
389 | #ifdef OPKELE_HAVE_KONFORKA | 395 | #ifdef OPKELE_HAVE_KONFORKA |
390 | }catch(konforka::exception& e) { | 396 | }catch(konforka::exception& e) { |
391 | #else | 397 | #else |
392 | }catch(std::exception& e){ | 398 | }catch(std::exception& e){ |
393 | #endif | 399 | #endif |
394 | DOUT_("Oops: " << e.what()); | 400 | DOUT_("Oops: " << e.what()); |
395 | cout << "Content-Type: text/plain\n\n" | 401 | cout << "Content-Type: text/plain\n\n" |
396 | "Exception:\n" | 402 | "Exception:\n" |
397 | " what: " << e.what() << endl; | 403 | " what: " << e.what() << endl; |
398 | #ifdef OPKELE_HAVE_KONFORKA | 404 | #ifdef OPKELE_HAVE_KONFORKA |
399 | cout << " where: " << e.where() << endl; | 405 | cout << " where: " << e.where() << endl; |
400 | if(!e._seen.empty()) { | 406 | if(!e._seen.empty()) { |
401 | cout << " seen:" << endl; | 407 | cout << " seen:" << endl; |
402 | for(list<konforka::code_point>::const_iterator | 408 | for(list<konforka::code_point>::const_iterator |
403 | i=e._seen.begin();i!=e._seen.end();++i) { | 409 | i=e._seen.begin();i!=e._seen.end();++i) { |
404 | cout << " " << i->c_str() << endl; | 410 | cout << " " << i->c_str() << endl; |
405 | } | 411 | } |
406 | } | 412 | } |
407 | #endif | 413 | #endif |
408 | } | 414 | } |
409 | } | 415 | } |