1 /*
2 * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2.0,
6 * as published by the Free Software Foundation.
7 *
8 * This program is also distributed with certain software (including
9 * but not limited to OpenSSL) that is licensed under separate terms,
10 * as designated in a particular file or component or in included license
11 * documentation. The authors of MySQL hereby grant you an additional
12 * permission to link the program and your derivative works with the
13 * separately licensed software that they have included with MySQL.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License, version 2.0, for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "plugin/x/src/sha256_password_cache.h"
26
27 #include "plugin/x/src/helper/multithread/rw_lock.h"
28 #include "plugin/x/src/xpl_log.h"
29
30 namespace xpl {
31
SHA256_password_cache()32 SHA256_password_cache::SHA256_password_cache()
33 : m_cache_lock(KEY_rwlock_x_sha256_password_cache) {}
34
35 /**
36 Start caching.
37
38 "Upsert" operation is going to cache all account informations passed to it.
39 */
enable()40 void SHA256_password_cache::enable() {
41 RWLock_writelock guard(&m_cache_lock);
42 m_accepting_input = true;
43 }
44
45 /**
46 Stop caching.
47
48 Remove all already cached accounts and prevent the "Upsert" operation from
49 caching account informations.
50 */
disable()51 void SHA256_password_cache::disable() {
52 RWLock_writelock guard(&m_cache_lock);
53 m_accepting_input = false;
54
55 m_password_cache.clear();
56 }
57
58 /**
59 Update a cache entry or add a new entry if there is no entry with the given
60 key. Key is constucted from username and hostname.
61
62 @param [in] user Username which will be used as a part of the cache entry key
63 @param [in] host Hostname which will be used as a part of the cache entry key
64 @param [in] value Value to be cached
65
66 @return Result of upsert operation
67 @retval true Value added or updated successfully
68 @retval false Upsert operation failed
69 */
upsert(const std::string & user,const std::string & host,const std::string & value)70 bool SHA256_password_cache::upsert(const std::string &user,
71 const std::string &host,
72 const std::string &value) {
73 auto key = create_key(user, host);
74 auto optional_hash = create_hash(value);
75 RWLock_writelock guard(&m_cache_lock);
76
77 if (!m_accepting_input) return false;
78
79 if (!optional_hash.first) return false;
80
81 m_password_cache[key] = optional_hash.second;
82 return true;
83 }
84
85 /**
86 Remove an entry from the cache
87
88 @param [in] user Username which will be used as a part of the cache entry key
89 @param [in] host Hostname which will be used as a part of the cache entry key
90
91 @return result of the deletion
92 @retval true Entry successfully removed
93 @retval false Error while removing the entry
94 */
remove(const std::string & user,const std::string & host)95 bool SHA256_password_cache::remove(const std::string &user,
96 const std::string &host) {
97 RWLock_writelock guard(&m_cache_lock);
98 return m_password_cache.erase(create_key(user, host));
99 }
100
101 /**
102 Try to get an entry from the cache.
103
104 @param [in] user Username which will be used as a part of the cache entry key
105 @param [in] host Hostname which will be used as a part of the cache entry key
106
107 @returns Pair containing flag and a string. When search was successful
108 returns true and cache entry value. Otherwise returns false and an empty
109 string
110 */
get_entry(const std::string & user,const std::string & host) const111 std::pair<bool, std::string> SHA256_password_cache::get_entry(
112 const std::string &user, const std::string &host) const {
113 RWLock_readlock guard(&m_cache_lock);
114
115 if (!m_accepting_input) return {false, ""};
116
117 const auto it = m_password_cache.find(create_key(user, host));
118
119 if (it == m_password_cache.end()) return {false, ""};
120
121 return {true, it->second};
122 }
123
124 /**
125 Check if hash of the given value is stored in the cache.
126
127 @param [in] user Username which will be used as a part of the cache entry key
128 @param [in] host Hostname which will be used as a part of the cache entry key
129 @param [in] value Value used as base for a hash which will be searched for
130
131 @retval true Value hash is stored in the cache
132 @retval false Value hash is not in the cache
133 */
contains(const std::string & user,const std::string & host,const std::string & value) const134 bool SHA256_password_cache::contains(const std::string &user,
135 const std::string &host,
136 const std::string &value) const {
137 const auto search_result = get_entry(user, host);
138
139 if (!search_result.first) return false;
140
141 const auto optional_hash = create_hash(value);
142
143 return !optional_hash.first ? false
144 : search_result.second == optional_hash.second;
145 }
146
147 /**
148 Remove all cache entries.
149 */
clear()150 void SHA256_password_cache::clear() {
151 RWLock_writelock guard(&m_cache_lock);
152
153 m_password_cache.clear();
154 }
155
156 /**
157 Create a key which will be used in the cache.
158
159 @param [in] user Username which will be used as a part of the cache entry key
160 @param [in] host Hostname which will be used as a part of the cache entry key
161
162 @returns Key string.
163 */
create_key(const std::string & user,const std::string & host) const164 std::string SHA256_password_cache::create_key(const std::string &user,
165 const std::string &host) const {
166 return user + '\0' + host + '\0';
167 }
168
169 /**
170 Create SHA256(SHA256(value)) hash from the given value. It will be used as
171 a cache entry
172
173 @param[in] value Value which will be used to calculate the hash
174
175 @returns Pair containing flag and a cache entry. Flag is returned so that in
176 case when hash could not be generated we will not insert empty string into
177 the password cache
178 */
179 std::pair<bool, SHA256_password_cache::sha2_cache_entry_t>
create_hash(const std::string & value) const180 SHA256_password_cache::create_hash(const std::string &value) const {
181 // Locking not needed since SHA256_digest does not contain any shared state
182 sha2_password::SHA256_digest sha256_digest;
183
184 const auto length = sha2_password::CACHING_SHA2_DIGEST_LENGTH;
185 unsigned char digest_buffer[length];
186
187 auto one_digest_round = [&](const std::string &value) {
188 if (sha256_digest.update_digest(value.c_str(), value.length()) ||
189 sha256_digest.retrieve_digest(digest_buffer, length)) {
190 return false;
191 }
192 return true;
193 };
194
195 // First digest round
196 if (!one_digest_round(value)) return {false, ""};
197
198 sha256_digest.scrub();
199
200 std::string first_digest_round = {std::begin(digest_buffer),
201 std::end(digest_buffer)};
202
203 // Second digest round
204 if (!one_digest_round(first_digest_round)) return {false, ""};
205
206 return {true, {std::begin(digest_buffer), std::end(digest_buffer)}};
207 }
208
209 } // namespace xpl
210