1 /*
2   Copyright (c) 2019, 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 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 "authentication.h"
26 
27 #include <memory>  // unique_ptr
28 #include <vector>
29 
30 #include <openssl/evp.h>
31 #include <openssl/opensslv.h>
32 
33 #include "mysql/harness/stdx/expected.h"
34 #include "mysql/harness/stdx/string_view.h"
35 
36 namespace impl {
37 /**
38  * scramble the password using the client's scheme.
39  *
40  * - mysql_native_password
41  *   - message-digest: SHA1
42  *   - inner message-digest-order: nonce + double_hashed_password
43  * - caching_sha256_password
44  *   - message-digest: SHA256
45  *   - inner message-digest-order: double_hashed_password + nonce
46  *
47  * @param nonce nonce used between server and client
48  * @param password cleartext password to be scrambled
49  * @param digest_func digest function
50  * @param nonce_before_double_hashed_password if true, nonce appears before
51  * double_hashed_password; if false, nonce appears after double_hashed_password
52  * @returns auth-response a client would send it
53  */
scramble(stdx::string_view nonce,stdx::string_view password,const EVP_MD * digest_func,bool nonce_before_double_hashed_password)54 static stdx::expected<std::vector<uint8_t>, void> scramble(
55     stdx::string_view nonce, stdx::string_view password,
56     const EVP_MD *digest_func, bool nonce_before_double_hashed_password) {
57   // in case of empty password, the hash is empty too
58   if (password.size() == 0) return {};
59 
60   const auto digest_size = EVP_MD_size(digest_func);
61 
62 #if OPENSSL_VERSION_NUMBER >= 0x1010000fL
63   std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)> digest_ctx(
64       EVP_MD_CTX_new(), &EVP_MD_CTX_free);
65 #else
66   std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_destroy)> digest_ctx(
67       EVP_MD_CTX_create(), &EVP_MD_CTX_destroy);
68 #endif
69 
70   std::vector<uint8_t> hashed_password(digest_size);
71   std::vector<uint8_t> double_hashed_password(digest_size);
72   std::vector<uint8_t> hashed_nonce_and_double_hashed_password(digest_size);
73 
74   int ok{1};
75   ok &= EVP_DigestInit_ex(digest_ctx.get(), digest_func, nullptr);
76   ok &= EVP_DigestUpdate(digest_ctx.get(), password.data(), password.size());
77   ok &= EVP_DigestFinal_ex(digest_ctx.get(), hashed_password.data(), nullptr);
78 
79   ok &= EVP_DigestInit_ex(digest_ctx.get(), digest_func, nullptr);
80   ok &= EVP_DigestUpdate(digest_ctx.get(), hashed_password.data(),
81                          hashed_password.size());
82   ok &= EVP_DigestFinal_ex(digest_ctx.get(), double_hashed_password.data(),
83                            nullptr);
84 
85   ok &= EVP_DigestInit_ex(digest_ctx.get(), digest_func, nullptr);
86 
87   if (nonce_before_double_hashed_password) {
88     ok &= EVP_DigestUpdate(digest_ctx.get(), nonce.data(), nonce.size());
89     ok &= EVP_DigestUpdate(digest_ctx.get(), double_hashed_password.data(),
90                            double_hashed_password.size());
91   } else {
92     ok &= EVP_DigestUpdate(digest_ctx.get(), double_hashed_password.data(),
93                            double_hashed_password.size());
94     ok &= EVP_DigestUpdate(digest_ctx.get(), nonce.data(), nonce.size());
95   }
96   ok &= EVP_DigestFinal_ex(digest_ctx.get(), double_hashed_password.data(),
97                            nullptr);
98 
99   if (!ok) {
100     return stdx::make_unexpected();
101   }
102 
103   // scramble the hashed password with the nonce
104   for (int i = 0; i < digest_size; ++i) {
105     hashed_password[i] ^= double_hashed_password[i];
106   }
107 
108   return hashed_password;
109 }
110 }  // namespace impl
111 
112 // mysql_native_password
113 
scramble(stdx::string_view nonce,stdx::string_view password)114 stdx::expected<std::vector<uint8_t>, void> MySQLNativePassword::scramble(
115     stdx::string_view nonce, stdx::string_view password) {
116   return impl::scramble(nonce, password, EVP_sha1(), true);
117 }
118 
119 constexpr char MySQLNativePassword::name[];
120 
121 // caching_sha2_password
122 
scramble(stdx::string_view nonce,stdx::string_view password)123 stdx::expected<std::vector<uint8_t>, void> CachingSha2Password::scramble(
124     stdx::string_view nonce, stdx::string_view password) {
125   return impl::scramble(nonce, password, EVP_sha256(), false);
126 }
127 constexpr char CachingSha2Password::name[];
128 
129 // clear_text_password
130 
scramble(stdx::string_view,stdx::string_view password)131 stdx::expected<std::vector<uint8_t>, void> ClearTextPassword::scramble(
132     stdx::string_view /* nonce */, stdx::string_view password) {
133   std::vector<uint8_t> res(password.begin(), password.end());
134 
135   // the payload always has a trailing \0
136   res.push_back(0);
137 
138   return res;
139 }
140 
141 constexpr char ClearTextPassword::name[];
142