1 /* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "sql/sql_alter_instance.h" /* Alter_instance class */
24
25 #include <utility>
26
27 #include "lex_string.h"
28 #include "m_string.h"
29 #include "mutex_lock.h"
30 #include "my_dbug.h"
31 #include "my_inttypes.h"
32 #include "my_sys.h" /* my_error */
33 #include "mysqld_error.h"
34 #include "sql/auth/auth_acls.h"
35 #include "sql/auth/sql_security_ctx.h"
36 #include "sql/derror.h" /* ER_THD */
37 #include "sql/handler.h" /* ha_resolve_by_legacy_type */
38 #include "sql/lock.h" /* acquire_shared_global_read_lock */
39 #include "sql/mysqld.h"
40 #include "sql/rpl_log_encryption.h"
41 #include "sql/sql_backup_lock.h" /* acquire_shared_backup_lock */
42 #include "sql/sql_class.h" /* THD */
43 #include "sql/sql_error.h"
44 #include "sql/sql_lex.h"
45 #include "sql/sql_plugin_ref.h"
46 #include "sql/sql_table.h" /* write_to_binlog */
47
48 /*
49 @brief
50 Log current command to binlog
51
52 @returns false on success,
53 true on error
54
55 In case of failure, appropriate error is logged.
56 */
57
log_to_binlog()58 bool Alter_instance::log_to_binlog() {
59 bool res = false;
60 if (!m_thd->lex->no_write_to_binlog)
61 res =
62 write_bin_log(m_thd, false, m_thd->query().str, m_thd->query().length);
63
64 return res;
65 }
66
67 /*
68 @brief
69 Executes master key rotation by calling SE api.
70
71 @returns false on success
72 true on error
73
74 In case of failure, appropriate error
75 is logged by function.
76 */
77
execute()78 bool Rotate_innodb_master_key::execute() {
79 const LEX_CSTRING storage_engine = {STRING_WITH_LEN("innodb")};
80 plugin_ref se_plugin;
81 handlerton *hton;
82
83 Security_context *sctx = m_thd->security_context();
84 if (!sctx->check_access(SUPER_ACL) &&
85 !sctx->has_global_grant(STRING_WITH_LEN("ENCRYPTION_KEY_ADMIN")).first) {
86 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0),
87 "SUPER or ENCRYPTION_KEY_ADMIN");
88 return true;
89 }
90
91 if ((se_plugin = ha_resolve_by_name(m_thd, &storage_engine, false))) {
92 hton = plugin_data<handlerton *>(se_plugin);
93 } else {
94 my_error(ER_MASTER_KEY_ROTATION_SE_UNAVAILABLE, MYF(0));
95 return true;
96 }
97
98 if (!hton->rotate_encryption_master_key) {
99 my_error(ER_MASTER_KEY_ROTATION_NOT_SUPPORTED_BY_SE, MYF(0));
100 return true;
101 }
102
103 /*
104 Acquire protection against GRL and check for concurrent change of read_only
105 value since encryption key rotation is not allowed in read_only/
106 super_read_only mode.
107 */
108 if (acquire_shared_global_read_lock(m_thd,
109 m_thd->variables.lock_wait_timeout)) {
110 // MDL subsystem has to set an error in Diagnostics Area
111 DBUG_ASSERT(m_thd->get_stmt_da()->is_error());
112 return true;
113 }
114
115 /*
116 Acquire shared backup lock to block concurrent backup. Acquire exclusive
117 backup lock to block any concurrent DDL. The fact that we acquire both
118 these locks also ensures that concurrent KEY rotation requests are blocked.
119 */
120 if (acquire_exclusive_backup_lock(m_thd, m_thd->variables.lock_wait_timeout,
121 true) ||
122 acquire_shared_backup_lock(m_thd, m_thd->variables.lock_wait_timeout)) {
123 // MDL subsystem has to set an error in Diagnostics Area
124 DBUG_ASSERT(m_thd->get_stmt_da()->is_error());
125 return true;
126 }
127
128 if (hton->rotate_encryption_master_key()) {
129 /* SE should have raised error */
130 DBUG_ASSERT(m_thd->get_stmt_da()->is_error());
131 return true;
132 }
133
134 if (log_to_binlog()) {
135 /*
136 Though we failed to write to binlog,
137 there is no way we can undo this operation.
138 So, covert error to a warning and let user
139 know that something went wrong while trying
140 to make entry in binlog.
141 */
142 m_thd->clear_error();
143 m_thd->get_stmt_da()->reset_diagnostics_area();
144
145 push_warning(m_thd, Sql_condition::SL_WARNING,
146 ER_MASTER_KEY_ROTATION_BINLOG_FAILED,
147 ER_THD(m_thd, ER_MASTER_KEY_ROTATION_BINLOG_FAILED));
148 }
149
150 my_ok(m_thd);
151 return false;
152 }
153
execute()154 bool Innodb_redo_log::execute() {
155 DBUG_TRACE;
156
157 const LEX_CSTRING storage_engine = {STRING_WITH_LEN("innodb")};
158
159 auto hton = plugin_data<handlerton *>(
160 ha_resolve_by_name(m_thd, &storage_engine, false));
161
162 if (hton == nullptr) {
163 /* Innodb engine is not loaded. Should never happen. */
164 my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), storage_engine.str);
165 }
166
167 Security_context *sctx = m_thd->security_context();
168 if (!sctx->has_global_grant(STRING_WITH_LEN("INNODB_REDO_LOG_ENABLE"))
169 .first) {
170 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "INNODB_REDO_LOG_ENABLE");
171 return true;
172 }
173
174 /*
175 Acquire shared backup lock to block concurrent backup. Acquire exclusive
176 backup lock to block any concurrent DDL. This would also serialize any
177 concurrent key rotation and other redo log enable/disable calls.
178 */
179 if (acquire_exclusive_backup_lock(m_thd, m_thd->variables.lock_wait_timeout,
180 true) ||
181 acquire_shared_backup_lock(m_thd, m_thd->variables.lock_wait_timeout)) {
182 DBUG_ASSERT(m_thd->get_stmt_da()->is_error());
183 return true;
184 }
185
186 if (hton->redo_log_set_state(m_thd, m_enable)) {
187 /* SE should have raised error */
188 DBUG_ASSERT(m_thd->get_stmt_da()->is_error());
189 return true;
190 }
191
192 /* Right now, we don't log this command to binary log as redo logging
193 options are low level physical attribute which is not needed to replicate
194 to other instances. */
195
196 my_ok(m_thd);
197 return false;
198 }
199
execute()200 bool Rotate_binlog_master_key::execute() {
201 DBUG_TRACE;
202
203 MUTEX_LOCK(lock, &LOCK_rotate_binlog_master_key);
204
205 Security_context *sctx = m_thd->security_context();
206 if (!sctx->check_access(SUPER_ACL) &&
207 !sctx->has_global_grant(STRING_WITH_LEN("BINLOG_ENCRYPTION_ADMIN"))
208 .first) {
209 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0),
210 "SUPER or BINLOG_ENCRYPTION_ADMIN");
211 return true;
212 }
213
214 if (!rpl_encryption.is_enabled()) {
215 my_error(ER_RPL_ENCRYPTION_CANNOT_ROTATE_BINLOG_MASTER_KEY, MYF(0));
216 return true;
217 }
218
219 if (rpl_encryption.remove_remaining_seqnos_from_keyring()) return true;
220
221 if (rpl_encryption.rotate_master_key()) return true;
222
223 my_ok(m_thd);
224 return false;
225 }
226