1 /* Copyright (c) 2011, 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 /**
24 @brief In here, we rewrite queries.
25
26 For now, this is only used to obfuscate passwords before we log a
27 statement. If we ever get other clients for rewriting, we should
28 introduce a rewrite_flags to determine what kind of rewriting
29 (password obfuscation etc.) is desired by the client.
30
31 Some items in the server can self-print anyway, but many can't.
32
33 For instance, you'll see a re-synthesized SELECT in EXPLAIN EXTENDED,
34 but you won't get a resynthized query in EXPLAIN EXTENDED if you
35 were explaining an UPDATE.
36
37 The following does not claim to be able to re-synthesize every
38 statement, but attempts to ultimately be able to resynthesize
39 all statements that have need of rewriting.
40
41 Stored procedures may also rewrite their statements (to show the actual
42 values of their variables etc.). There is currently no scenario where
43 a statement can be eligible for both rewrites (see sp_instr.cc).
44 Special consideration will need to be taken if this is intenionally
45 changed at a later date. (There is an ASSERT() in place that will
46 hopefully catch unintentional changes.)
47
48 Finally, sp_* have code to print a stored program for use by
49 SHOW PROCEDURE CODE / SHOW FUNCTION CODE.
50
51 Thus, regular query parsing comes through here for logging.
52 So does prepared statement logging.
53 Stored instructions of the sp_instr_stmt type (which should
54 be the only ones to contain passwords, and therefore at this
55 time be eligible for rewriting) go through the regular parsing
56 facilities and therefore also come through here for logging
57 (other sp_instr_* types don't).
58
59 Finally, as rewriting goes, by default we replace the password with a literal
60 \<secret\>, with *no* quotation marks so the statement would fail if the
61 user were to cut & paste it without filling in the real password. This
62 default behavior is ON for rewriting to the textual logs. For instance :
63 General, slow query and audit log. Rewriters also have a provision to
64 replace the password with its hash where we have the latter. (so they
65 could be replayed, IDENTIFIED WITH \<plugin_name\> AS \<hash\>);
66 This hash is needed while writing the statements for binlog.
67 */
68
69 #include "sql/sql_rewrite.h"
70
71 #include <stdio.h>
72 #include <string.h>
73 #include <sys/types.h>
74 #include <algorithm>
75 #include <memory>
76 #include <set>
77 #include <string>
78
79 #include "lex_string.h"
80 #include "m_ctype.h"
81 #include "m_string.h"
82 #include "my_compiler.h"
83 #include "my_dbug.h"
84 #include "my_inttypes.h"
85 #include "prealloced_array.h"
86 #include "sql/auth/auth_acls.h"
87 #include "sql/auth/auth_common.h" // GRANT_ACL
88 #include "sql/handler.h"
89 #include "sql/log_event.h" // append_query_string
90 #include "sql/rpl_slave.h" // SLAVE_SQL, SLAVE_IO
91 #include "sql/set_var.h"
92 #include "sql/sql_admin.h" // Sql_cmd_clone
93 #include "sql/sql_class.h" // THD
94 #include "sql/sql_connect.h"
95 #include "sql/sql_lex.h" // LEX
96 #include "sql/sql_list.h"
97 #include "sql/sql_parse.h" // get_current_user
98 #include "sql/sql_servers.h"
99 #include "sql/sql_show.h" // append_identifier
100 #include "sql/table.h"
101 #include "sql_string.h" // String
102 #include "violite.h"
103
104 #ifndef DBUG_OFF
105 #define HASH_STRING_WITH_QUOTE \
106 "$5$BVZy9O>'a+2MH]_?$fpWyabcdiHjfCVqId/quykZzjaA7adpkcen/uiQrtmOK4p4"
107 #endif
108
109 namespace {
110 /**
111 Append a comma to given string if item wasn't the first to be added.
112
113 @param[in,out] str The string to (maybe) append to.
114 @param[in,out] comma If true, there are already items in the list.
115 Always true afterwards.
116 */
comma_maybe(String * str,bool * comma)117 void comma_maybe(String *str, bool *comma) {
118 if (*comma)
119 str->append(STRING_WITH_LEN(", "));
120 else
121 *comma = true;
122 }
123
124 /**
125 Append a key/value pair to a string, with an optional preceeding comma.
126 For numeric values.
127
128 @param[in,out] str The string to append to
129 @param comma Prepend a comma?
130 @param txt C-string, must end in a space
131 @param len strlen(txt)
132 @param val numeric value
133 @param cond only append if this evaluates to true
134
135 @retval false if any subsequent key/value pair would be the first
136 */
137
append_int(String * str,bool comma,const char * txt,size_t len,long val,int cond)138 bool append_int(String *str, bool comma, const char *txt, size_t len, long val,
139 int cond) {
140 if (cond) {
141 String numbuf(42);
142 comma_maybe(str, &comma);
143 str->append(txt, len);
144 str->append(STRING_WITH_LEN(" "));
145 numbuf.set((longlong)val, &my_charset_bin);
146 str->append(numbuf);
147 return true;
148 }
149 return comma;
150 }
151
152 /**
153 Append a key/value pair to a string if the value is non-NULL,
154 with an optional preceding comma.
155
156 @param[in,out] str The string to append to
157 @param comma Prepend a comma?
158 @param key C-string: the key, must be non-NULL
159 @param val C-string: the value
160
161 @retval false if any subsequent key/value pair would be the first
162 */
163
append_str(String * str,bool comma,const char * key,const char * val)164 bool append_str(String *str, bool comma, const char *key, const char *val) {
165 if (val) {
166 comma_maybe(str, &comma);
167 str->append(key);
168 str->append(STRING_WITH_LEN(" '"));
169 str->append(val);
170 str->append(STRING_WITH_LEN("'"));
171 return true;
172 }
173 return comma;
174 }
175 /**
176 Append the authorization id for the user
177
178 @param [in] thd The THD to find the SQL mode
179 @param [in] user LEX User to retrieve the plugin string
180 @param [in] comma Separator to be prefixed before adding user info
181 @param [in, out] str The string in which authID is suffixed
182 */
append_auth_id(const THD * thd,const LEX_USER * user,bool comma,String * str)183 void append_auth_id(const THD *thd, const LEX_USER *user, bool comma,
184 String *str) {
185 DBUG_ASSERT(thd);
186 String from_user(user->user.str, user->user.length, system_charset_info);
187 String from_host(user->host.str, user->host.length, system_charset_info);
188 if (comma) str->append(',');
189 append_query_string(thd, system_charset_info, &from_user, str);
190 str->append(STRING_WITH_LEN("@"));
191 append_query_string(thd, system_charset_info, &from_host, str);
192 }
193 /**
194 Used with List<>::sort for alphabetic sorting of LEX_USER records
195 using user,host as keys.
196
197 @param l1 A LEX_USER element
198 @param l2 A LEX_USER element
199
200 @retval 1 if n1 > n2
201 @retval 0 if n1 <= n2
202 */
lex_user_comp(LEX_USER * l1,LEX_USER * l2)203 int lex_user_comp(LEX_USER *l1, LEX_USER *l2) {
204 size_t length = std::min(l1->user.length, l2->user.length);
205 int key = memcmp(l1->user.str, l2->user.str, length);
206 if (key == 0 && l1->user.length == l2->user.length) {
207 length = std::min(l1->host.length, l2->host.length);
208 key = memcmp(l1->host.str, l2->host.str, length);
209 if (key == 0 && l1->host.length == l2->host.length) return 0;
210 }
211 if (key == 0)
212 return (l1->user.length > l2->user.length ? 1 : 0);
213 else
214 return (key > 0 ? 1 : 0);
215 }
216 /**
217 Util method which does the real rewrite of the SQL statment.
218 If a Rewriter is available for the specified SQL command then
219 the rewritten query will be stored in the String rlb; otherwise,
220 the string will just be cleared.
221
222 @param thd The THD to rewrite for.
223 @param type Purpose of rewriting the query
224 @param params Wrapper object of parameters in case needed by a SQL
225 rewriter.
226 @param[in,out] rlb Buffer to return the rewritten query in.
227 Will be empty if no rewriting happened.
228 @retval true If the Query is re-written.
229 @retval false Otherwise
230 */
rewrite_query(THD * thd,Consumer_type type,Rewrite_params * params,String & rlb)231 bool rewrite_query(THD *thd, Consumer_type type, Rewrite_params *params,
232 String &rlb) {
233 DBUG_TRACE;
234 std::unique_ptr<I_rewriter> rw = nullptr;
235 bool rewrite = false;
236
237 rlb.length(0);
238
239 switch (thd->lex->sql_command) {
240 case SQLCOM_GRANT:
241 rw.reset(new Rewriter_grant(thd, type, params));
242 break;
243 case SQLCOM_SET_PASSWORD:
244 rw.reset(new Rewriter_set_password(thd, type, params));
245 break;
246 case SQLCOM_SET_OPTION:
247 rw.reset(new Rewriter_set(thd, type));
248 break;
249 case SQLCOM_CREATE_USER:
250 rw.reset(new Rewriter_create_user(thd, type));
251 break;
252 case SQLCOM_ALTER_USER:
253 rw.reset(new Rewriter_alter_user(thd, type));
254 break;
255 case SQLCOM_SHOW_CREATE_USER:
256 rw.reset(new Rewriter_show_create_user(thd, type, params));
257 break;
258 case SQLCOM_CHANGE_MASTER:
259 rw.reset(new Rewriter_change_master(thd, type));
260 break;
261 case SQLCOM_SLAVE_START:
262 rw.reset(new Rewriter_slave_start(thd, type));
263 break;
264 case SQLCOM_CREATE_SERVER:
265 rw.reset(new Rewriter_create_server(thd, type));
266 break;
267 case SQLCOM_ALTER_SERVER:
268 rw.reset(new Rewriter_alter_server(thd, type));
269 break;
270
271 /*
272 PREPARE stmt FROM <string> is rewritten so that <string> is
273 not logged. The statement in <string> will in turn be logged
274 by the prepare and the execute functions in sql_prepare.cc.
275 They do call rewrite so they can safely log the statement,
276 but when they call us, it'll be with sql_command set to reflect
277 the statement in question, not SQLCOM_PREPARE or SQLCOM_EXECUTE.
278 Therefore, there is no SQLCOM_EXECUTE case here, and all
279 SQLCOM_PREPARE does is remove <string>; the "other half",
280 i.e. printing what string we prepare from happens when the
281 prepare function calls the logger (and comes by here with
282 sql_command set to the command being prepared).
283 */
284 case SQLCOM_PREPARE:
285 rw.reset(new Rewriter_prepare(thd, type));
286 break;
287 case SQLCOM_START_GROUP_REPLICATION:
288 rw.reset(new Rewriter_start_group_replication(thd, type));
289 break;
290 case SQLCOM_CLONE: {
291 rw.reset(new Rewriter_clone(thd, type));
292 break;
293 }
294 default: /* unhandled query types are legal. */
295 break;
296 }
297 if (rw) rewrite = rw->rewrite(rlb);
298
299 return rewrite;
300 }
301 } // anonymous namespace
302
303 /**
304 Provides the default interface to rewrite the SQL statements to
305 to obfuscate passwords.
306
307 The query aimed to be rewritten in the usual log files
308 (i.e. General, slow query and audit log) uses default value of
309 type which is Consumer_type::LOG
310
311 Side-effects:
312
313 - thd->m_rewritten_query will contain a rewritten query,
314 or be cleared if no rewriting took place.
315 LOCK_thd_query will be temporarily acquired to make that change.
316
317 @note Keep in mind that these side-effects will only happen when
318 calling this top-level function, but not when calling
319 individual sub-functions directly!
320
321 @param thd The THD to rewrite for.
322 @param type Purpose of rewriting the query
323 Consumer_type::LOG
324 To rewrite the query either for general, slow query
325 and audit log.
326 Consumer_type::BINLOG
327 To rewrite the query for binlogs.
328 Consumer_type::CONSOLE
329 To rewrite the query for standard output.
330 @param params Wrapper object of parameters in case needed by a SQL
331 rewriter.
332 */
mysql_rewrite_query(THD * thd,Consumer_type type,Rewrite_params * params)333 void mysql_rewrite_query(THD *thd, Consumer_type type /*= Consumer_type::LOG */,
334 Rewrite_params *params /*= nullptr*/) {
335 String rlb;
336
337 DBUG_TRACE;
338 DBUG_ASSERT(thd);
339
340 // We should not come through here twice for the same query.
341 DBUG_ASSERT(thd->rewritten_query().length() == 0);
342
343 if (thd->lex->contains_plaintext_password) {
344 rewrite_query(thd, type, params, rlb);
345 if (rlb.length() > 0) thd->swap_rewritten_query(rlb);
346 // The previous rewritten query is in rlb now, which now goes out of scope.
347 }
348 }
349 /**
350 Provides the default interface to rewrite the ACL query.
351
352 @param thd The THD to rewrite for.
353 @param rlb Buffer to return rewritten query in (if any) if
354 do_ps_instrument is false.
355 @param type Purpose of rewriting the query
356 Consumer_type::LOG
357 To rewrite the query either for general, slow query
358 and audit log.
359 Consumer_type::BINLOG
360 To rewrite the query for binlogs.
361 Consumer_type::CONSOLE
362 To rewrite the query for standard output.
363 @param params Wrapper object of parameters in case needed by a SQL
364 rewriter.
365 @param do_ps_instrument flag to indicate if the query has to be instrumented
366 in the PSI. Default value is true.
367 If instrumented, the previous
368 */
mysql_rewrite_acl_query(THD * thd,String & rlb,Consumer_type type,Rewrite_params * params,bool do_ps_instrument)369 void mysql_rewrite_acl_query(THD *thd, String &rlb, Consumer_type type,
370 Rewrite_params *params /* = nullptr */,
371 bool do_ps_instrument /* = true */) {
372 if (rewrite_query(thd, type, params, rlb) && (rlb.length() > 0) &&
373 do_ps_instrument) {
374 thd->swap_rewritten_query(rlb);
375 thd->set_query_for_display(thd->rewritten_query().ptr(),
376 thd->rewritten_query().length());
377 /*
378 rlb now contains the previous rewritten query.
379 We clear it here both to save memory and to prevent possible confusion.
380 */
381 rlb.mem_free();
382 }
383 }
384
I_rewriter(THD * thd,Consumer_type type)385 I_rewriter::I_rewriter(THD *thd, Consumer_type type)
386 : m_thd(thd), m_consumer_type(type) {
387 DBUG_ASSERT(thd);
388 }
389
~I_rewriter()390 I_rewriter::~I_rewriter() {}
391 /**
392 Reset the previous consumer type.
393
394 @param [in] type new consumer type for which query is to be rewritten
395 */
set_consumer_type(Consumer_type type)396 void I_rewriter::set_consumer_type(Consumer_type type) {
397 m_consumer_type = type;
398 }
399 /**
400 Return the current consumer type set in the object
401
402 @retval Consumer type set currently.
403 */
consumer_type()404 Consumer_type I_rewriter::consumer_type() { return m_consumer_type; }
405 /* Constructor */
Rewriter_user(THD * thd,Consumer_type type)406 Rewriter_user::Rewriter_user(THD *thd, Consumer_type type)
407 : I_rewriter(thd, type) {}
408 /**
409 Appends the essential clauses for SHOW CREATE|CREATE|ALTER USER statements
410 in the buffer rlb.
411
412 @param[in,out] rlb Buffer to return the rewritten query in.
413
414 @retval false Since it does the partial query rewrite.
415 Must be called through derived classes rewrite().
416 */
rewrite(String & rlb) const417 bool Rewriter_user::rewrite(String &rlb) const {
418 LEX *lex = m_thd->lex;
419 rewrite_users(lex, &rlb);
420 rewrite_default_roles(lex, &rlb);
421 rewrite_ssl_properties(lex, &rlb);
422 rewrite_user_resources(lex, &rlb);
423 rewrite_password_expired(lex, &rlb);
424 rewrite_account_lock(lex, &rlb);
425 rewrite_password_history(lex, &rlb);
426 rewrite_password_reuse(lex, &rlb);
427 rewrite_password_require_current(lex, &rlb);
428 rewrite_account_lock_state(lex, &rlb);
429 rewrite_user_application_user_metadata(lex, &rlb);
430 return false;
431 }
432
433 /**
434 Use the LEX for reconstructing the ATTRIBUTE or COMMENT clause.
435 @param [in] lex LEX struct to know if the clause was specified
436 @param [in, out] str The string in which the clause is suffixed
437 */
rewrite_in_memory_user_application_user_metadata(const LEX * lex,String * str) const438 void Rewriter_user::rewrite_in_memory_user_application_user_metadata(
439 const LEX *lex, String *str) const {
440 if (lex->alter_user_attribute ==
441 enum_alter_user_attribute::ALTER_USER_ATTRIBUTE) {
442 str->append(" ATTRIBUTE '");
443 } else if (lex->alter_user_attribute ==
444 enum_alter_user_attribute::ALTER_USER_COMMENT) {
445 str->append(" COMMENT '");
446 }
447 if (lex->alter_user_attribute !=
448 enum_alter_user_attribute::ALTER_USER_COMMENT_NOT_USED) {
449 str->append(lex->alter_user_comment_text);
450 str->append("'");
451 }
452 }
453
454 /**
455 Default implementaiton of the the rewriter for user applicatiton
456 user metadata.
457 @param [in] lex LEX struct to know if the clause was specified
458 @param [in, out] str The string in which the clause is suffixed
459 */
rewrite_user_application_user_metadata(const LEX * lex,String * str) const460 void Rewriter_create_user::rewrite_user_application_user_metadata(
461 const LEX *lex, String *str) const {
462 parent::rewrite_in_memory_user_application_user_metadata(lex, str);
463 }
464
465 /**
466 Default implementaiton of the the rewriter for user applicatiton
467 user metadata.
468 @param [in] lex LEX struct to know if the clause was specified
469 @param [in, out] str The string in which the clause is suffixed
470 */
rewrite_user_application_user_metadata(const LEX * lex,String * str) const471 void Rewriter_alter_user::rewrite_user_application_user_metadata(
472 const LEX *lex, String *str) const {
473 parent::rewrite_in_memory_user_application_user_metadata(lex, str);
474 }
475
476 /**
477 Append the literal \<secret\> in place of password to the output string
478
479 @param [in, out] str The string in which literal value is suffixed
480 */
append_literal_secret(String * str) const481 void Rewriter_user::append_literal_secret(String *str) const {
482 str->append(STRING_WITH_LEN("<secret>"));
483 }
484 /**
485 Append the password hash to the output string
486
487 @param [in] user LEX_USER to fetch the auth string of it.
488 @param [in, out] str The string in which hash value is suffixed
489 */
490
append_auth_str(LEX_USER * user,String * str) const491 void Rewriter_user::append_auth_str(LEX_USER *user, String *str) const {
492 String from_auth(user->auth.str, user->auth.length, system_charset_info);
493 append_query_string(m_thd, system_charset_info, &from_auth, str);
494 }
495 /**
496 Append the SSL clause for users iff it is specified
497
498 @param [in] lex LEX struct to check if clause is specified
499 @param [in, out] str The string in which clause is suffixed
500 */
rewrite_ssl_properties(const LEX * lex,String * str) const501 void Rewriter_user::rewrite_ssl_properties(const LEX *lex, String *str) const {
502 if (lex->ssl_type != SSL_TYPE_NOT_SPECIFIED) {
503 str->append(STRING_WITH_LEN(" REQUIRE"));
504 switch (lex->ssl_type) {
505 case SSL_TYPE_SPECIFIED:
506 if (lex->x509_subject) {
507 str->append(STRING_WITH_LEN(" SUBJECT '"));
508 str->append(lex->x509_subject);
509 str->append(STRING_WITH_LEN("'"));
510 }
511 if (lex->x509_issuer) {
512 str->append(STRING_WITH_LEN(" ISSUER '"));
513 str->append(lex->x509_issuer);
514 str->append(STRING_WITH_LEN("'"));
515 }
516 if (lex->ssl_cipher) {
517 str->append(STRING_WITH_LEN(" CIPHER '"));
518 str->append(lex->ssl_cipher);
519 str->append(STRING_WITH_LEN("'"));
520 }
521 break;
522 case SSL_TYPE_X509:
523 str->append(STRING_WITH_LEN(" X509"));
524 break;
525 case SSL_TYPE_ANY:
526 str->append(STRING_WITH_LEN(" SSL"));
527 break;
528 case SSL_TYPE_NONE:
529 str->append(STRING_WITH_LEN(" NONE"));
530 break;
531 default:
532 DBUG_ASSERT(false);
533 break;
534 }
535 }
536 }
537 /**
538 Append the user resource clauses for users
539
540 @param [in] lex LEX struct to check if clause is specified
541 @param [in, out] str The string in which clause is suffixed
542 */
rewrite_user_resources(const LEX * lex,String * str) const543 void Rewriter_user::rewrite_user_resources(const LEX *lex, String *str) const {
544 if (lex->mqh.specified_limits) {
545 str->append(" WITH");
546 append_int(str, false, STRING_WITH_LEN(" MAX_QUERIES_PER_HOUR"),
547 lex->mqh.questions,
548 lex->mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR);
549
550 append_int(str, false, STRING_WITH_LEN(" MAX_UPDATES_PER_HOUR"),
551 lex->mqh.updates,
552 lex->mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR);
553
554 append_int(
555 str, false, STRING_WITH_LEN(" MAX_CONNECTIONS_PER_HOUR"),
556 lex->mqh.conn_per_hour,
557 lex->mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR);
558
559 append_int(str, false, STRING_WITH_LEN(" MAX_USER_CONNECTIONS"),
560 lex->mqh.user_conn,
561 lex->mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS);
562 }
563 }
564 /**
565 Append the ACCOUNT LOCK clause for users iff it is specified
566
567 @param [in] lex LEX struct to check if clause is specified
568 @param [in, out] str The string in which clause is suffixed
569 */
rewrite_account_lock(const LEX * lex,String * str) const570 void Rewriter_user::rewrite_account_lock(const LEX *lex, String *str) const {
571 if (lex->alter_password.update_account_locked_column) {
572 if (lex->alter_password.account_locked) {
573 str->append(STRING_WITH_LEN(" ACCOUNT LOCK"));
574 } else {
575 str->append(STRING_WITH_LEN(" ACCOUNT UNLOCK"));
576 }
577 }
578 }
579 /**
580 Append the PASSWORD EXPIRE clause for users iff it is specified
581
582 @param [in] lex LEX struct to check if clause is specified
583 @param [in, out] str The string in which clause is suffixed
584 */
rewrite_password_expired(const LEX * lex,String * str) const585 void Rewriter_user::rewrite_password_expired(const LEX *lex,
586 String *str) const {
587 if (lex->alter_password.update_password_expired_fields) {
588 if (lex->alter_password.update_password_expired_column) {
589 str->append(STRING_WITH_LEN(" PASSWORD EXPIRE"));
590 } else if (lex->alter_password.expire_after_days) {
591 append_int(str, false, STRING_WITH_LEN(" PASSWORD EXPIRE INTERVAL"),
592 lex->alter_password.expire_after_days, true);
593 str->append(STRING_WITH_LEN(" DAY"));
594 } else if (lex->alter_password.use_default_password_lifetime) {
595 str->append(STRING_WITH_LEN(" PASSWORD EXPIRE DEFAULT"));
596 } else {
597 str->append(STRING_WITH_LEN(" PASSWORD EXPIRE NEVER"));
598 }
599 }
600 }
601 /**
602 Append the PASSWORD REQUIRE CURRENT clause for users
603 @param [in] lex LEX struct to know if the clause was specified
604 @param [in, out] str The string in which the clause is suffixed
605 */
rewrite_password_require_current(LEX * lex,String * str) const606 void Rewriter_user::rewrite_password_require_current(LEX *lex,
607 String *str) const {
608 switch (lex->alter_password.update_password_require_current) {
609 case Lex_acl_attrib_udyn::YES:
610 str->append(STRING_WITH_LEN(" PASSWORD REQUIRE CURRENT"));
611 break;
612 case Lex_acl_attrib_udyn::DEFAULT:
613 str->append(STRING_WITH_LEN(" PASSWORD REQUIRE CURRENT DEFAULT"));
614 break;
615 case Lex_acl_attrib_udyn::NO:
616 str->append(STRING_WITH_LEN(" PASSWORD REQUIRE CURRENT OPTIONAL"));
617 break;
618 case Lex_acl_attrib_udyn::UNCHANGED:
619 // Do nothing
620 break;
621 default:
622 DBUG_ASSERT(false);
623 }
624 }
625
626 /**
627 Append the account lock state
628
629 Append FAILED_LOGIN_ATTEMPTS/PASSWORD_LOCK_TIME if account auto-lock
630 is active.
631
632 @param [in] lex LEX to retrieve data from
633 @param [in, out] str The string in which plugin info is suffixed
634 */
rewrite_account_lock_state(LEX * lex,String * str) const635 void Rewriter_user::rewrite_account_lock_state(LEX *lex, String *str) const {
636 if (lex->alter_password.update_failed_login_attempts) {
637 append_int(str, false, STRING_WITH_LEN(" FAILED_LOGIN_ATTEMPTS"),
638 lex->alter_password.failed_login_attempts, true);
639 }
640 if (lex->alter_password.update_password_lock_time) {
641 if (lex->alter_password.password_lock_time >= 0)
642 append_int(str, false, STRING_WITH_LEN(" PASSWORD_LOCK_TIME"),
643 lex->alter_password.password_lock_time, true);
644 else
645 str->append(STRING_WITH_LEN(" PASSWORD_LOCK_TIME UNBOUNDED"));
646 }
647 }
648
649 /**
650 Append the authentication plugin name for the user
651
652 @param [in] user LEX User to retrieve the plugin string
653 @param [in, out] str The string in which plugin info is suffixed
654 */
append_plugin_name(const LEX_USER * user,String * str) const655 void Rewriter_user::append_plugin_name(const LEX_USER *user,
656 String *str) const {
657 str->append(STRING_WITH_LEN(" WITH "));
658 if (user->plugin.length > 0) {
659 String from_plugin(user->plugin.str, user->plugin.length,
660 system_charset_info);
661 append_query_string(m_thd, system_charset_info, &from_plugin, str);
662 } else {
663 std::string def_plugin_name = get_default_autnetication_plugin_name();
664 String default_plugin(def_plugin_name.c_str(), def_plugin_name.length(),
665 system_charset_info);
666 append_query_string(m_thd, system_charset_info, &default_plugin, str);
667 }
668 }
669 /**
670 The default implementation is to append the PASSWORD HISTORY clause iff it
671 is specified. Though concrete classes may add their own implementation.
672
673 @param [in] lex LEX struct to check if clause is specified
674 @param [in, out] str The string in which clause is suffixed
675 */
rewrite_password_history(const LEX * lex,String * str) const676 void Rewriter_user::rewrite_password_history(const LEX *lex,
677 String *str) const {
678 if (lex->alter_password.use_default_password_history) {
679 str->append(STRING_WITH_LEN(" PASSWORD HISTORY DEFAULT"));
680 } else {
681 append_int(str, false, STRING_WITH_LEN(" PASSWORD HISTORY"),
682 lex->alter_password.password_history_length, true);
683 }
684 }
685 /**
686 The default implementation is to append the PASSWORD REUSE clause iff it is
687 specified. Though concrete classes may add their own implementation.
688
689 @param [in] lex LEX struct to check if clause is specified
690 @param [in, out] str The string in which clause is suffixed
691 */
rewrite_password_reuse(const LEX * lex,String * str) const692 void Rewriter_user::rewrite_password_reuse(const LEX *lex, String *str) const {
693 if (lex->alter_password.use_default_password_reuse_interval) {
694 str->append(STRING_WITH_LEN(" PASSWORD REUSE INTERVAL DEFAULT"));
695 } else {
696 append_int(str, false, STRING_WITH_LEN(" PASSWORD REUSE INTERVAL"),
697 lex->alter_password.password_reuse_interval, true);
698 str->append(STRING_WITH_LEN(" DAY"));
699 }
700 }
701
702 /**
703 Fetch the users from user_list in LEX struct and append them to the String.
704
705 @param [in] lex LEX struct to check if clause is specified
706 @param [in, out] str The string in which clause is suffixed
707 */
rewrite_users(LEX * lex,String * str) const708 void Rewriter_user::rewrite_users(LEX *lex, String *str) const {
709 bool comma = false;
710 LEX_USER *user_name, *tmp_user_name;
711 List_iterator<LEX_USER> user_list(lex->users_list);
712 while ((tmp_user_name = user_list++)) {
713 if ((user_name = get_current_user(m_thd, tmp_user_name))) {
714 append_user_auth_info(user_name, comma, str);
715 comma = true;
716 }
717 }
718 }
719
720 /**
721 Append the DEFAULT ROLE clause for users iff it is specified
722
723 @param [in] lex LEX struct to check if clause is specified
724 @param [in, out] str The string in which clause is suffixed
725 */
rewrite_default_roles(const LEX * lex,String * str) const726 void Rewriter_user::rewrite_default_roles(const LEX *lex, String *str) const {
727 bool comma = false;
728 if (lex->default_roles && lex->default_roles->elements > 0) {
729 str->append(" DEFAULT ROLE ");
730 lex->default_roles->sort(&lex_user_comp);
731 List_iterator<LEX_USER> role_it(*(lex->default_roles));
732 LEX_USER *role;
733 while ((role = role_it++)) {
734 if (comma) str->append(',');
735 str->append(create_authid_str_from(role).c_str());
736 comma = true;
737 }
738 }
739 }
740
Rewriter_create_user(THD * thd,Consumer_type type)741 Rewriter_create_user::Rewriter_create_user(THD *thd, Consumer_type type)
742 : Rewriter_user(thd, type) {}
743
744 /**
745 Rewrite the query for the CREATE USER statement.
746
747 @param[in,out] rlb Buffer to return the rewritten query in.
748
749 @retval true The query was rewritten.
750 */
rewrite(String & rlb) const751 bool Rewriter_create_user::rewrite(String &rlb) const {
752 LEX *lex = m_thd->lex;
753 rlb.append("CREATE USER ");
754
755 if (lex->create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
756 rlb.append("IF NOT EXISTS ");
757
758 parent::rewrite(rlb);
759 return true;
760 }
761 /**
762 Append the authID, plugin and auth str of the user to output string :
763 - If the corresponding clause is specified.
764 - Always add the plugin info for the target type BINLOG
765 - Add the literal \<secret\> in place of plain text password
766 for the target type LOG
767
768 @param [in] user Lex user to fetch the info
769 @param [in] comma separator to be prefixed while appending user info
770 @param [in, out] str String to which user auth info is suffixed.
771 */
append_user_auth_info(LEX_USER * user,bool comma,String * str) const772 void Rewriter_create_user::append_user_auth_info(LEX_USER *user, bool comma,
773 String *str) const {
774 append_auth_id(m_thd, user, comma, str);
775 if (user->uses_identified_by_clause || user->uses_identified_with_clause ||
776 user->uses_authentication_string_clause ||
777 m_consumer_type == Consumer_type::BINLOG) {
778 str->append(STRING_WITH_LEN(" IDENTIFIED"));
779 if (user->uses_identified_with_clause ||
780 m_consumer_type == Consumer_type::BINLOG)
781 append_plugin_name(user, str);
782
783 if (user->uses_identified_by_clause &&
784 m_consumer_type == Consumer_type::TEXTLOG) {
785 str->append(STRING_WITH_LEN(" BY "));
786 append_literal_secret(str);
787 } else if (user->auth.length > 0) {
788 str->append(STRING_WITH_LEN(" AS "));
789 append_auth_str(user, str);
790 }
791 }
792 }
793 /**
794 Append the PASSWORD HISTORY clause for users iff it is specified
795
796 @param [in] lex LEX struct to check if clause is specified
797 @param [in, out] str The string in which clause is suffixed
798 */
rewrite_password_history(const LEX * lex,String * str) const799 void Rewriter_create_user::rewrite_password_history(const LEX *lex,
800 String *str) const {
801 if (lex->alter_password.update_password_history) {
802 parent::rewrite_password_history(lex, str);
803 }
804 }
805 /**
806 Append the PASSWORD REUSE clause for users iff it is specified
807
808 @param [in] lex LEX struct to check if clause is specified
809 @param [in, out] str The string in which clause is suffixed
810 */
rewrite_password_reuse(const LEX * lex,String * str) const811 void Rewriter_create_user::rewrite_password_reuse(const LEX *lex,
812 String *str) const {
813 if (lex->alter_password.update_password_reuse_interval) {
814 parent::rewrite_password_reuse(lex, str);
815 }
816 }
Rewriter_alter_user(THD * thd,Consumer_type type)817 Rewriter_alter_user::Rewriter_alter_user(THD *thd, Consumer_type type)
818 : Rewriter_user(thd, type) {}
819
820 /**
821 Rewrite the query for the ALTER USER statement.
822
823 @param[in,out] rlb Buffer to return the rewritten query in.
824
825 @retval true The query was rewritten.
826 */
rewrite(String & rlb) const827 bool Rewriter_alter_user::rewrite(String &rlb) const {
828 LEX *lex = m_thd->lex;
829 rlb.append("ALTER USER ");
830
831 if (lex->drop_if_exists) rlb.append("IF EXISTS ");
832
833 parent::rewrite(rlb);
834 return true;
835 }
836 /**
837 Append the authID, plugin and auth str of the user to output string :
838 - If the corresponding clause is specified.
839 - Always add the plugin info for the target type BINLOG
840 - Add the literal \<secret\> in place of plain text password
841 for the target type LOG
842
843 @param [in] user Lex user to fetch the info
844 @param [in] comma separator to be prefixed while appending user info
845 @param [in, out] str String to which user auth info is suffixed.
846 */
append_user_auth_info(LEX_USER * user,bool comma,String * str) const847 void Rewriter_alter_user::append_user_auth_info(LEX_USER *user, bool comma,
848 String *str) const {
849 append_auth_id(m_thd, user, comma, str);
850 if (user->uses_identified_by_clause || user->uses_identified_with_clause ||
851 user->uses_authentication_string_clause) {
852 str->append(STRING_WITH_LEN(" IDENTIFIED"));
853 if (user->uses_identified_with_clause ||
854 m_consumer_type == Consumer_type::BINLOG)
855 append_plugin_name(user, str);
856
857 if (user->uses_identified_by_clause &&
858 m_consumer_type == Consumer_type::TEXTLOG) {
859 str->append(STRING_WITH_LEN(" BY "));
860 append_literal_secret(str);
861 /* Add the literal value in place of current password in the textlogs. */
862 if (user->uses_replace_clause) {
863 str->append(STRING_WITH_LEN(" REPLACE <secret>"));
864 }
865 } else if (user->auth.length > 0) {
866 str->append(STRING_WITH_LEN(" AS "));
867 append_auth_str(user, str);
868 }
869
870 if (user->retain_current_password) {
871 str->append(STRING_WITH_LEN(" RETAIN CURRENT PASSWORD"));
872 }
873 }
874
875 if (user->discard_old_password) {
876 str->append(STRING_WITH_LEN(" DISCARD OLD PASSWORD"));
877 }
878 }
879 /**
880 Append the PASSWORD HISTORY clause for users iff it is specified
881
882 @param [in] lex LEX struct to check if clause is specified
883 @param [in, out] str The string in which clause is suffixed
884 */
rewrite_password_history(const LEX * lex,String * str) const885 void Rewriter_alter_user::rewrite_password_history(const LEX *lex,
886 String *str) const {
887 if (lex->alter_password.update_password_history) {
888 parent::rewrite_password_history(lex, str);
889 }
890 }
891 /**
892 Append the PASSWORD REUSE clause for users iff it is specified
893
894 @param [in] lex LEX struct to check if clause is specified
895 @param [in, out] str The string in which clause is suffixed
896 */
rewrite_password_reuse(const LEX * lex,String * str) const897 void Rewriter_alter_user::rewrite_password_reuse(const LEX *lex,
898 String *str) const {
899 if (lex->alter_password.update_password_reuse_interval) {
900 parent::rewrite_password_reuse(lex, str);
901 }
902 }
Rewriter_show_create_user(THD * thd,Consumer_type type,Rewrite_params * params)903 Rewriter_show_create_user::Rewriter_show_create_user(THD *thd,
904 Consumer_type type,
905 Rewrite_params *params)
906 : Rewriter_user(thd, type),
907 show_params_(dynamic_cast<Show_user_params *>(params)) {}
908
909 /**
910 Rewrite the query for the SHOW CREATE USER statement.
911 This method takes an additional parameter from the
912 Show_user_params to reconstruct the ATTRIBUTE clause.
913 These parameters must be read form disk which is done
914 in mysql_show_create_user()
915
916 @sa mysql_show_create_user()
917
918 @param[in,out] rlb Buffer to return the rewritten query in.
919
920 @retval true The query was rewritten.
921 */
rewrite(String & rlb) const922 bool Rewriter_show_create_user::rewrite(String &rlb) const {
923 rlb.append("CREATE USER ");
924 parent::rewrite(rlb);
925 return true;
926 }
927
928 /**
929 Overrides implementaiton of the the rewriter for user application
930 user metadata. This is needed because we have to read the
931 ATTRIBUTE data from disk.
932 @param [in] lex LEX struct to know if the clause was specified
933 @param [in, out] str The string in which the clause is suffixed
934 */
rewrite_user_application_user_metadata(const LEX * lex MY_ATTRIBUTE ((unused)),String * str) const935 void Rewriter_show_create_user::rewrite_user_application_user_metadata(
936 const LEX *lex MY_ATTRIBUTE((unused)), String *str) const {
937 /* Only show the ATTRIBUTE operator if there's any attribute to show. */
938 if (show_params_->metadata_str->length() > 0) {
939 str->append(" ATTRIBUTE '");
940 str->append(*show_params_->metadata_str);
941 str->append("'");
942 }
943 }
944
945 /**
946 A special rewriter override to make SHOW CREATE USER convert the string
947 to hex if print_identified_with_as hex is on
948
949 @param [in] user LEX_USER to fetch the auth string of it.
950 @param [in, out] str The string in which hash value is suffixed
951
952 @sa Rewriter_user::append_auth_str
953 */
append_auth_str(LEX_USER * user,String * str) const954 void Rewriter_show_create_user::append_auth_str(LEX_USER *user,
955 String *str) const {
956 String from_auth(user->auth.str, user->auth.length, system_charset_info);
957
958 if (show_params_ && show_params_->print_identified_with_as_hex_ &&
959 user->auth.length) {
960 for (const char *c = user->auth.str;
961 static_cast<size_t>(c - user->auth.str) < user->auth.length; c++) {
962 if (!my_isgraph(system_charset_info, *c)) {
963 from_auth.alloc(user->auth.length * 2 + 3);
964 str_to_hex(from_auth.c_ptr_quick(), user->auth.str, user->auth.length);
965 from_auth.length(user->auth.length * 2 + 2);
966 str->append(from_auth);
967
968 return;
969 }
970 }
971 }
972 append_query_string(m_thd, system_charset_info, &from_auth, str);
973 }
974 /**
975 Append the PASSWORD HISTORY clause for users
976
977 @param [in] lex LEX struct to check if clause is specified
978 @param [in, out] str The string in which clause is suffixed
979 */
rewrite_password_history(const LEX * lex,String * str) const980 void Rewriter_show_create_user::rewrite_password_history(const LEX *lex,
981 String *str) const {
982 parent::rewrite_password_history(lex, str);
983 }
984 /**
985 Append the PASSWORD REUSE clause for users
986
987 @param [in] lex LEX struct to check if clause is specified
988 @param [in, out] str The string in which clause is suffixed
989 */
rewrite_password_reuse(const LEX * lex,String * str) const990 void Rewriter_show_create_user::rewrite_password_reuse(const LEX *lex,
991 String *str) const {
992 parent::rewrite_password_reuse(lex, str);
993 }
994 /**
995 Append the authID, plugin name and suth str user to output string
996
997 @param [in] user Lex user to fetch the info
998 @param [in] comma separator to be prefixed while appending user info
999 @param [in, out] str String to which user auth info is suffixed.
1000 */
append_user_auth_info(LEX_USER * user,bool comma,String * str) const1001 void Rewriter_show_create_user::append_user_auth_info(LEX_USER *user,
1002 bool comma,
1003 String *str) const {
1004 append_auth_id(m_thd, user, comma, str);
1005 DBUG_ASSERT(m_thd->lex->contains_plaintext_password == false);
1006 str->append(STRING_WITH_LEN(" IDENTIFIED"));
1007 append_plugin_name(user, str);
1008 if (user->auth.length > 0) {
1009 str->append(STRING_WITH_LEN(" AS "));
1010 if (show_params_ && show_params_->hide_password_hash) {
1011 append_literal_secret(str);
1012 } else {
1013 append_auth_str(user, str);
1014 }
1015 }
1016 }
1017
Rewriter_set(THD * thd,Consumer_type type)1018 Rewriter_set::Rewriter_set(THD *thd, Consumer_type type)
1019 : I_rewriter(thd, type) {}
1020
1021 /**
1022 Rewrite the query for the SET statement.
1023
1024 @param[in,out] rlb Buffer to return the rewritten query in.
1025
1026 @retval true the query was rewritten
1027 @retval false otherwise
1028 */
rewrite(String & rlb) const1029 bool Rewriter_set::rewrite(String &rlb) const {
1030 LEX *lex = m_thd->lex;
1031 List_iterator_fast<set_var_base> it(lex->var_list);
1032 set_var_base *var;
1033 bool comma = false;
1034
1035 rlb.append(STRING_WITH_LEN("SET "));
1036
1037 while ((var = it++)) {
1038 comma_maybe(&rlb, &comma);
1039 var->print(m_thd, &rlb);
1040 }
1041 return true;
1042 }
1043
Rewriter_set_password(THD * thd,Consumer_type type,Rewrite_params * params)1044 Rewriter_set_password::Rewriter_set_password(THD *thd, Consumer_type type,
1045 Rewrite_params *params)
1046 : Rewriter_set(thd, type) {
1047 User_params *param = dynamic_cast<User_params *>(params);
1048 if (param) m_users = param->users;
1049 }
1050
1051 /**
1052 Rewrite the query for the SET PASSWORD statement.
1053
1054 @param[in,out] rlb Buffer to return the rewritten query in.
1055
1056 @retval true the query was rewritten
1057 @retval false otherwise
1058 */
rewrite(String & rlb) const1059 bool Rewriter_set_password::rewrite(String &rlb) const {
1060 bool ret_val = false;
1061 if (m_consumer_type == Consumer_type::BINLOG) {
1062 if (m_users == nullptr || m_users->size() == 0) return ret_val;
1063
1064 /* SET PASSWORD should always have one user */
1065 DBUG_ASSERT(m_users->size() == 1);
1066 bool set_temp_string = false;
1067 /*
1068 Setting this flag will generate the password hash string which
1069 contains a single quote.
1070 */
1071 DBUG_EXECUTE_IF("force_hash_string_with_quote", set_temp_string = true;);
1072 LEX_USER *user = *(m_users->begin());
1073 String current_user(user->user.str, user->user.length, system_charset_info);
1074 String current_host(user->host.str, user->host.length, system_charset_info);
1075 String auth_str;
1076 if (set_temp_string) {
1077 #ifndef DBUG_OFF
1078 auth_str = String(HASH_STRING_WITH_QUOTE, strlen(HASH_STRING_WITH_QUOTE),
1079 system_charset_info);
1080 #endif
1081 } else {
1082 auth_str = String(user->auth.str, user->auth.length, system_charset_info);
1083 }
1084 /*
1085 Construct :
1086 ALTER USER '<user>'@'<host>' IDENTIFIED WITH '<plugin>' AS '<HASH>'
1087 */
1088 rlb.append(STRING_WITH_LEN("ALTER USER "));
1089 append_query_string(m_thd, system_charset_info, ¤t_user, &rlb);
1090 rlb.append(STRING_WITH_LEN("@"));
1091 append_query_string(m_thd, system_charset_info, ¤t_host, &rlb);
1092 rlb.append(STRING_WITH_LEN(" IDENTIFIED WITH '"));
1093 rlb.append(user->plugin.str);
1094 rlb.append(STRING_WITH_LEN("' AS "));
1095 append_query_string(m_thd, system_charset_info, &auth_str, &rlb);
1096 if (user->retain_current_password)
1097 rlb.append(STRING_WITH_LEN(" RETAIN CURRENT PASSWORD"));
1098 ret_val = true;
1099 } else {
1100 ret_val = parent::rewrite(rlb);
1101 }
1102
1103 return ret_val;
1104 }
1105
Rewriter_grant(THD * thd,Consumer_type type,Rewrite_params * params)1106 Rewriter_grant::Rewriter_grant(THD *thd, Consumer_type type,
1107 Rewrite_params *params)
1108 : I_rewriter(thd, type) {
1109 grant_params = dynamic_cast<Grant_params *>(params);
1110 }
1111
1112 /**
1113 Rewrite the query for the GRANT statement.
1114
1115 @param[in,out] rlb Buffer to return the rewritten query in.
1116
1117 @retval true the query was rewritten
1118 */
rewrite(String & rlb) const1119 bool Rewriter_grant::rewrite(String &rlb) const {
1120 LEX *lex = m_thd->lex;
1121
1122 TABLE_LIST *first_table = lex->select_lex->table_list.first;
1123 bool proxy_grant = lex->type == TYPE_ENUM_PROXY;
1124 String cols(1024);
1125 int c;
1126
1127 auto append_authids = [&rlb](THD *thd, List_iterator<LEX_USER> &list) {
1128 LEX_USER *user_name, *tmp_user_name;
1129 bool comma = false;
1130 while ((tmp_user_name = list++)) {
1131 if ((user_name = get_current_user(thd, tmp_user_name))) {
1132 append_auth_id(thd, user_name, comma, &rlb);
1133 comma = true;
1134 }
1135 }
1136 };
1137
1138 rlb.append(STRING_WITH_LEN("GRANT "));
1139
1140 if (proxy_grant)
1141 rlb.append(STRING_WITH_LEN("PROXY"));
1142 else if (lex->all_privileges)
1143 rlb.append(STRING_WITH_LEN("ALL PRIVILEGES"));
1144 else if (lex->grant_privilege)
1145 rlb.append(STRING_WITH_LEN("GRANT OPTION"));
1146 else {
1147 bool comma = false;
1148 ulong priv;
1149
1150 for (c = 0, priv = SELECT_ACL; priv <= GLOBAL_ACLS; c++, priv <<= 1) {
1151 if (priv == GRANT_ACL) continue;
1152
1153 bool comma_inner = false;
1154
1155 if (lex->columns.elements) // show columns, if any
1156 {
1157 class LEX_COLUMN *column;
1158 List_iterator<LEX_COLUMN> column_iter(lex->columns);
1159
1160 cols.length(0);
1161 cols.append(STRING_WITH_LEN(" ("));
1162
1163 /*
1164 If the statement was GRANT SELECT(f2), INSERT(f3), UPDATE(f1,f3, f2),
1165 our list cols will contain the order f2, f3, f1, and thus that's
1166 the order we'll recreate the privilege: UPDATE (f2, f3, f1)
1167 */
1168
1169 while ((column = column_iter++)) {
1170 if (column->rights & priv) {
1171 comma_maybe(&cols, &comma_inner);
1172 append_identifier(m_thd, &cols, column->column.ptr(),
1173 column->column.length());
1174 }
1175 }
1176 cols.append(STRING_WITH_LEN(")"));
1177 }
1178
1179 if (comma_inner || (lex->grant & priv)) // show privilege name
1180 {
1181 comma_maybe(&rlb, &comma);
1182 rlb.append(global_acls_vector[c].c_str(),
1183 global_acls_vector[c].length());
1184 if (!(lex->grant & priv)) // general outranks specific
1185 rlb.append(cols);
1186 }
1187 }
1188 /* List extended global privilege IDs */
1189 if (!first_table && !lex->current_select()->db) {
1190 List_iterator<LEX_CSTRING> it(lex->dynamic_privileges);
1191 LEX_CSTRING *privilege;
1192 while ((privilege = it++)) {
1193 comma_maybe(&rlb, &comma);
1194 rlb.append(privilege->str, privilege->length);
1195 }
1196 }
1197 if (!comma) // no privs, default to USAGE
1198 rlb.append(STRING_WITH_LEN("USAGE"));
1199 }
1200
1201 rlb.append(STRING_WITH_LEN(" ON "));
1202 switch (lex->type) {
1203 case TYPE_ENUM_PROCEDURE:
1204 rlb.append(STRING_WITH_LEN("PROCEDURE "));
1205 break;
1206 case TYPE_ENUM_FUNCTION:
1207 rlb.append(STRING_WITH_LEN("FUNCTION "));
1208 break;
1209 default:
1210 break;
1211 }
1212
1213 LEX_USER *user_name, *tmp_user_name;
1214 List_iterator<LEX_USER> user_list(lex->users_list);
1215
1216 if (proxy_grant) {
1217 bool comma = false;
1218 tmp_user_name = user_list++;
1219 user_name = get_current_user(m_thd, tmp_user_name);
1220 if (user_name) append_auth_id(m_thd, user_name, comma, &rlb);
1221 } else if (first_table) {
1222 if (first_table->is_view()) {
1223 append_identifier(m_thd, &rlb, first_table->view_db.str,
1224 first_table->view_db.length);
1225 rlb.append(STRING_WITH_LEN("."));
1226 append_identifier(m_thd, &rlb, first_table->view_name.str,
1227 first_table->view_name.length);
1228 } else {
1229 append_identifier(m_thd, &rlb, first_table->db, strlen(first_table->db));
1230 rlb.append(STRING_WITH_LEN("."));
1231 append_identifier(m_thd, &rlb, first_table->table_name,
1232 strlen(first_table->table_name));
1233 }
1234 } else {
1235 if (lex->current_select()->db)
1236 append_identifier(m_thd, &rlb, lex->current_select()->db,
1237 strlen(lex->current_select()->db));
1238 else
1239 rlb.append("*");
1240 rlb.append(STRING_WITH_LEN(".*"));
1241 }
1242
1243 rlb.append(STRING_WITH_LEN(" TO "));
1244
1245 append_authids(m_thd, user_list);
1246 if (lex->grant & GRANT_ACL) {
1247 rlb.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
1248 }
1249
1250 /*
1251 AS ... clause is added in following cases
1252 1. User has explicitly executed GRANT ... AS ...
1253 In this case we write it as it.
1254 2. --partial_revokes is ON and we are rewritting
1255 GRANT for binary log.
1256 */
1257 if (grant_params != nullptr) {
1258 LEX_GRANT_AS *grant_as = grant_params->grant_as_info;
1259 if (grant_params->grant_as_provided ||
1260 (m_consumer_type == Consumer_type::BINLOG && grant_as &&
1261 grant_as->grant_as_used)) {
1262 if ((user_name = get_current_user(m_thd, grant_as->user))) {
1263 rlb.append(STRING_WITH_LEN(" AS "));
1264 append_auth_id(m_thd, user_name, false, &rlb);
1265 rlb.append(STRING_WITH_LEN(" WITH ROLE "));
1266 switch (grant_as->role_type) {
1267 case role_enum::ROLE_DEFAULT:
1268 rlb.append(STRING_WITH_LEN("DEFAULT"));
1269 break;
1270 case role_enum::ROLE_ALL:
1271 rlb.append(STRING_WITH_LEN("ALL"));
1272 if (grant_as->role_list) {
1273 rlb.append(STRING_WITH_LEN(" EXCEPT "));
1274 List_iterator<LEX_USER> role_list(*grant_as->role_list);
1275 append_authids(m_thd, role_list);
1276 }
1277 break;
1278 case role_enum::ROLE_NAME: {
1279 List_iterator<LEX_USER> role_list(*grant_as->role_list);
1280 append_authids(m_thd, role_list);
1281 } break;
1282 case role_enum::ROLE_NONE:
1283 rlb.append(STRING_WITH_LEN("NONE"));
1284 break;
1285 default:
1286 DBUG_ASSERT(false);
1287 rlb.append(STRING_WITH_LEN("NONE"));
1288 break;
1289 }
1290 }
1291 }
1292 }
1293 return true;
1294 }
1295
Rewriter_change_master(THD * thd,Consumer_type type)1296 Rewriter_change_master::Rewriter_change_master(THD *thd, Consumer_type type)
1297 : I_rewriter(thd, type) {}
1298
1299 /**
1300 Rewrite the query for the CHANGE MASTER statement.
1301
1302 @param[in,out] rlb Buffer to return the rewritten query in.
1303
1304 @retval true the query was rewritten
1305 @retval false otherwise
1306 */
rewrite(String & rlb) const1307 bool Rewriter_change_master::rewrite(String &rlb) const {
1308 LEX *lex = m_thd->lex;
1309 rlb.append(STRING_WITH_LEN("CHANGE MASTER TO "));
1310 bool comma = false;
1311 comma = append_str(&rlb, comma, "MASTER_BIND =", lex->mi.bind_addr);
1312 comma = append_str(&rlb, comma, "MASTER_HOST =", lex->mi.host);
1313 comma = append_str(&rlb, comma, "MASTER_USER =", lex->mi.user);
1314
1315 if (lex->mi.password) {
1316 comma_maybe(&rlb, &comma);
1317 rlb.append(STRING_WITH_LEN("MASTER_PASSWORD = <secret>"));
1318 }
1319 comma = append_int(&rlb, comma, STRING_WITH_LEN("MASTER_PORT ="),
1320 lex->mi.port, lex->mi.port > 0);
1321 // condition as per rpl_slave.cc
1322 comma = append_int(&rlb, comma, STRING_WITH_LEN("MASTER_CONNECT_RETRY ="),
1323 lex->mi.connect_retry, lex->mi.connect_retry > 0);
1324 comma = append_int(
1325 &rlb, comma, STRING_WITH_LEN("MASTER_RETRY_COUNT ="), lex->mi.retry_count,
1326 lex->mi.retry_count_opt != LEX_MASTER_INFO::LEX_MI_UNCHANGED);
1327 // MASTER_DELAY 0..MASTER_DELAY_MAX; -1 == unspecified
1328 comma = append_int(&rlb, comma, STRING_WITH_LEN("MASTER_DELAY ="),
1329 lex->mi.sql_delay, lex->mi.sql_delay >= 0);
1330
1331 if (lex->mi.heartbeat_opt != LEX_MASTER_INFO::LEX_MI_UNCHANGED) {
1332 comma_maybe(&rlb, &comma);
1333 rlb.append(STRING_WITH_LEN("MASTER_HEARTBEAT_PERIOD = "));
1334 if (lex->mi.heartbeat_opt == LEX_MASTER_INFO::LEX_MI_DISABLE)
1335 rlb.append(STRING_WITH_LEN("0"));
1336 else {
1337 char buf[64];
1338 snprintf(buf, 64, "%f", lex->mi.heartbeat_period);
1339 rlb.append(buf);
1340 }
1341 }
1342
1343 // log file (slave I/O thread)
1344 comma = append_str(&rlb, comma, "MASTER_LOG_FILE =", lex->mi.log_file_name);
1345 // MASTER_LOG_POS is >= BIN_LOG_HEADER_SIZE; 0 == unspecified in stmt.
1346 comma = append_int(&rlb, comma, STRING_WITH_LEN("MASTER_LOG_POS ="),
1347 lex->mi.pos, lex->mi.pos != 0);
1348 comma = append_int(
1349 &rlb, comma, STRING_WITH_LEN("MASTER_AUTO_POSITION ="),
1350 (lex->mi.auto_position == LEX_MASTER_INFO::LEX_MI_ENABLE) ? 1 : 0,
1351 lex->mi.auto_position != LEX_MASTER_INFO::LEX_MI_UNCHANGED);
1352
1353 // log file (slave SQL thread)
1354 comma = append_str(&rlb, comma, "RELAY_LOG_FILE =", lex->mi.relay_log_name);
1355 // RELAY_LOG_POS is >= BIN_LOG_HEADER_SIZE; 0 == unspecified in stmt.
1356 comma = append_int(&rlb, comma, STRING_WITH_LEN("RELAY_LOG_POS ="),
1357 lex->mi.relay_log_pos, lex->mi.relay_log_pos != 0);
1358
1359 // SSL
1360 comma = append_int(&rlb, comma, STRING_WITH_LEN("MASTER_SSL ="),
1361 lex->mi.ssl == LEX_MASTER_INFO::LEX_MI_ENABLE ? 1 : 0,
1362 lex->mi.ssl != LEX_MASTER_INFO::LEX_MI_UNCHANGED);
1363 comma = append_str(&rlb, comma, "MASTER_SSL_CA =", lex->mi.ssl_ca);
1364 comma = append_str(&rlb, comma, "MASTER_SSL_CAPATH =", lex->mi.ssl_capath);
1365 comma = append_str(&rlb, comma, "MASTER_SSL_CERT =", lex->mi.ssl_cert);
1366 comma = append_str(&rlb, comma, "MASTER_SSL_CRL =", lex->mi.ssl_crl);
1367 comma = append_str(&rlb, comma, "MASTER_SSL_CRLPATH =", lex->mi.ssl_crlpath);
1368 comma = append_str(&rlb, comma, "MASTER_SSL_KEY =", lex->mi.ssl_key);
1369 comma = append_str(&rlb, comma, "MASTER_SSL_CIPHER =", lex->mi.ssl_cipher);
1370 comma = append_int(
1371 &rlb, comma, STRING_WITH_LEN("MASTER_SSL_VERIFY_SERVER_CERT ="),
1372 (lex->mi.ssl_verify_server_cert == LEX_MASTER_INFO::LEX_MI_ENABLE) ? 1
1373 : 0,
1374 lex->mi.ssl_verify_server_cert != LEX_MASTER_INFO::LEX_MI_UNCHANGED);
1375
1376 comma = append_str(&rlb, comma, "MASTER_TLS_VERSION =", lex->mi.tls_version);
1377 if (LEX_MASTER_INFO::SPECIFIED_NULL == lex->mi.tls_ciphersuites) {
1378 comma_maybe(&rlb, &comma);
1379 rlb.append(STRING_WITH_LEN("MASTER_TLS_CIPHERSUITES = NULL"));
1380 } else if (LEX_MASTER_INFO::SPECIFIED_STRING == lex->mi.tls_ciphersuites) {
1381 comma = append_str(&rlb, comma, "MASTER_TLS_CIPHERSUITES =",
1382 lex->mi.tls_ciphersuites_string);
1383 }
1384
1385 // Public key
1386 comma = append_str(&rlb, comma,
1387 "MASTER_PUBLIC_KEY_PATH =", lex->mi.public_key_path);
1388 comma = append_int(
1389 &rlb, comma, STRING_WITH_LEN("GET_MASTER_PUBLIC_KEY ="),
1390 (lex->mi.get_public_key == LEX_MASTER_INFO::LEX_MI_ENABLE) ? 1 : 0,
1391 lex->mi.get_public_key != LEX_MASTER_INFO::LEX_MI_UNCHANGED);
1392
1393 // IGNORE_SERVER_IDS
1394 if (lex->mi.repl_ignore_server_ids_opt != LEX_MASTER_INFO::LEX_MI_UNCHANGED) {
1395 bool comma_list = false;
1396
1397 comma_maybe(&rlb, &comma);
1398 rlb.append(STRING_WITH_LEN("IGNORE_SERVER_IDS = ( "));
1399
1400 for (size_t i = 0; i < lex->mi.repl_ignore_server_ids.size(); i++) {
1401 ulong s_id = lex->mi.repl_ignore_server_ids[i];
1402 comma_maybe(&rlb, &comma_list);
1403 rlb.append_ulonglong(s_id);
1404 }
1405 rlb.append(STRING_WITH_LEN(" )"));
1406 }
1407 if (lex->mi.compression_algorithm)
1408 comma = append_str(&rlb, comma, "MASTER_COMPRESSION_ALGORITHMS = ",
1409 lex->mi.compression_algorithm);
1410 comma = append_int(
1411 &rlb, comma, STRING_WITH_LEN("MASTER_ZSTD_COMPRESSION_LEVEL = "),
1412 lex->mi.zstd_compression_level, lex->mi.zstd_compression_level != 0);
1413
1414 /* channel options -- no preceding comma here! */
1415 if (lex->mi.for_channel)
1416 append_str(&rlb, false, " FOR CHANNEL", lex->mi.channel);
1417 return true;
1418 }
1419
Rewriter_slave_start(THD * thd,Consumer_type type)1420 Rewriter_slave_start::Rewriter_slave_start(THD *thd, Consumer_type type)
1421 : I_rewriter(thd, type) {}
1422
1423 /**
1424 Rewrite the query for the SLAVE START statement.
1425
1426 @param[in,out] rlb Buffer to return the rewritten query in.
1427
1428 @retval true The query was rewritten.
1429 */
rewrite(String & rlb) const1430 bool Rewriter_slave_start::rewrite(String &rlb) const {
1431 LEX *lex = m_thd->lex;
1432
1433 rlb.append(STRING_WITH_LEN("START SLAVE"));
1434
1435 /* thread_types */
1436
1437 if (lex->slave_thd_opt & SLAVE_IO) rlb.append(STRING_WITH_LEN(" IO_THREAD"));
1438
1439 if (lex->slave_thd_opt & SLAVE_IO && lex->slave_thd_opt & SLAVE_SQL)
1440 rlb.append(STRING_WITH_LEN(","));
1441
1442 if (lex->slave_thd_opt & SLAVE_SQL)
1443 rlb.append(STRING_WITH_LEN(" SQL_THREAD"));
1444
1445 /* UNTIL options */
1446
1447 // GTID
1448 if (lex->mi.gtid) {
1449 rlb.append((lex->mi.gtid_until_condition ==
1450 LEX_MASTER_INFO::UNTIL_SQL_BEFORE_GTIDS)
1451 ? " UNTIL SQL_BEFORE_GTIDS"
1452 : " UNTIL SQL_AFTER_GTIDS");
1453 append_str(&rlb, false, " =", lex->mi.gtid);
1454 }
1455
1456 // SQL_AFTER_MTS_GAPS
1457 else if (lex->mi.until_after_gaps) {
1458 rlb.append(STRING_WITH_LEN(" UNTIL SQL_AFTER_MTS_GAPS"));
1459 }
1460
1461 // MASTER_LOG_FILE/POS
1462 else if (lex->mi.log_file_name) {
1463 append_str(&rlb, false, " UNTIL MASTER_LOG_FILE =", lex->mi.log_file_name);
1464 append_int(&rlb, true, STRING_WITH_LEN("MASTER_LOG_POS ="), lex->mi.pos,
1465 lex->mi.pos > 0);
1466 }
1467
1468 // RELAY_LOG_FILE/POS
1469 else if (lex->mi.relay_log_name) {
1470 append_str(&rlb, false, " UNTIL RELAY_LOG_FILE =", lex->mi.relay_log_name);
1471 append_int(&rlb, true, STRING_WITH_LEN("RELAY_LOG_POS ="),
1472 lex->mi.relay_log_pos, lex->mi.relay_log_pos > 0);
1473 }
1474
1475 /* connection options */
1476 append_str(&rlb, false, " USER =", lex->slave_connection.user);
1477
1478 if (lex->slave_connection.password)
1479 rlb.append(STRING_WITH_LEN(" PASSWORD = <secret>"));
1480
1481 append_str(&rlb, false, " DEFAULT_AUTH =", lex->slave_connection.plugin_auth);
1482 append_str(&rlb, false, " PLUGIN_DIR =", lex->slave_connection.plugin_dir);
1483
1484 /* channel options */
1485 if (lex->mi.for_channel)
1486 append_str(&rlb, false, " FOR CHANNEL", lex->mi.channel);
1487 return true;
1488 }
1489
Rewriter_server_option(THD * thd,Consumer_type type)1490 Rewriter_server_option::Rewriter_server_option(THD *thd, Consumer_type type)
1491 : I_rewriter(thd, type) {}
Rewriter_create_server(THD * thd,Consumer_type type)1492 Rewriter_create_server::Rewriter_create_server(THD *thd, Consumer_type type)
1493 : Rewriter_server_option(thd, type) {}
1494 /**
1495 Append the SERVER OPTIONS clause
1496
1497 @param [in] lex Lex structure
1498 @param [in,out] str A String object to append the rewritten query in.
1499 */
mysql_rewrite_server_options(const LEX * lex,String * str) const1500 void Rewriter_server_option::mysql_rewrite_server_options(const LEX *lex,
1501 String *str) const {
1502 str->append(STRING_WITH_LEN(" OPTIONS ( "));
1503 str->append(STRING_WITH_LEN("PASSWORD <secret>"));
1504 append_str(str, true, "USER", lex->server_options.get_username());
1505 append_str(str, true, "HOST", lex->server_options.get_host());
1506 append_str(str, true, "DATABASE", lex->server_options.get_db());
1507 append_str(str, true, "OWNER", lex->server_options.get_owner());
1508 append_str(str, true, "SOCKET", lex->server_options.get_socket());
1509 append_int(str, true, STRING_WITH_LEN("PORT"), lex->server_options.get_port(),
1510 lex->server_options.get_port() != Server_options::PORT_NOT_SET);
1511 str->append(STRING_WITH_LEN(" )"));
1512 }
1513 /**
1514 Rewrite the query for the CREATE SERVER statement.
1515
1516 @param[in,out] rlb Buffer to return the rewritten query in.
1517
1518 @retval true the query was rewritten
1519 @retval false otherwise
1520 */
rewrite(String & rlb) const1521 bool Rewriter_create_server::rewrite(String &rlb) const {
1522 LEX *lex = m_thd->lex;
1523
1524 if (!lex->server_options.get_password()) return false;
1525
1526 rlb.append(STRING_WITH_LEN("CREATE SERVER "));
1527 rlb.append(lex->server_options.m_server_name.str
1528 ? lex->server_options.m_server_name.str
1529 : "");
1530 rlb.append(STRING_WITH_LEN(" FOREIGN DATA WRAPPER '"));
1531 rlb.append(lex->server_options.get_scheme() ? lex->server_options.get_scheme()
1532 : "");
1533 rlb.append(STRING_WITH_LEN("'"));
1534 parent::mysql_rewrite_server_options(lex, &rlb);
1535
1536 return true;
1537 }
1538
Rewriter_alter_server(THD * thd,Consumer_type type)1539 Rewriter_alter_server::Rewriter_alter_server(THD *thd, Consumer_type type)
1540 : Rewriter_server_option(thd, type) {}
1541
1542 /**
1543 Rewrite the query for the ALTER SERVER statement.
1544
1545 @param[in,out] rlb Buffer to return the rewritten query in.
1546
1547 @retval true the query was rewritten
1548 @retval false otherwise
1549 */
rewrite(String & rlb) const1550 bool Rewriter_alter_server::rewrite(String &rlb) const {
1551 LEX *lex = m_thd->lex;
1552
1553 if (!lex->server_options.get_password()) return false;
1554
1555 rlb.append(STRING_WITH_LEN("ALTER SERVER "));
1556 rlb.append(lex->server_options.m_server_name.str
1557 ? lex->server_options.m_server_name.str
1558 : "");
1559 parent::mysql_rewrite_server_options(lex, &rlb);
1560
1561 return true;
1562 }
1563
Rewriter_prepare(THD * thd,Consumer_type type)1564 Rewriter_prepare::Rewriter_prepare(THD *thd, Consumer_type type)
1565 : I_rewriter(thd, type) {}
1566
1567 /**
1568 Rewrite the query for the PREPARE statement.
1569
1570 @param[in,out] rlb Buffer to return the rewritten query in.
1571
1572 @retval true the query was rewritten
1573 @retval false otherwise
1574 */
rewrite(String & rlb) const1575 bool Rewriter_prepare::rewrite(String &rlb) const {
1576 LEX *lex = m_thd->lex;
1577
1578 if (lex->prepared_stmt_code_is_varref) return false;
1579
1580 rlb.append(STRING_WITH_LEN("PREPARE "));
1581 rlb.append(lex->prepared_stmt_name.str, lex->prepared_stmt_name.length);
1582 rlb.append(STRING_WITH_LEN(" FROM ..."));
1583 return true;
1584 }
1585
Rewriter_clone(THD * thd,Consumer_type type)1586 Rewriter_clone::Rewriter_clone(THD *thd, Consumer_type type)
1587 : I_rewriter(thd, type) {}
1588
1589 /**
1590 Rewrite the query for the CLONE statement to hide password.
1591
1592 @param[in,out] rlb Buffer to return the rewritten query in.
1593
1594 @retval true the query was rewritten
1595 @retval false otherwise
1596 */
rewrite(String & rlb) const1597 bool Rewriter_clone::rewrite(String &rlb) const {
1598 auto clone_cmd = dynamic_cast<Sql_cmd_clone *>(m_thd->lex->m_sql_cmd);
1599 return (clone_cmd->rewrite(m_thd, rlb));
1600 }
1601
Rewriter_start_group_replication(THD * thd,Consumer_type type)1602 Rewriter_start_group_replication::Rewriter_start_group_replication(
1603 THD *thd, Consumer_type type)
1604 : I_rewriter(thd, type) {}
1605
1606 /**
1607 Rewrite the query for the START GROUP_REPLICATION command.
1608
1609 @param[in,out] rlb Buffer to return the rewritten query in.
1610
1611 @retval true the query is rewritten
1612 */
rewrite(String & rlb) const1613 bool Rewriter_start_group_replication::rewrite(String &rlb) const {
1614 LEX *lex = m_thd->lex;
1615 bool comma = false;
1616
1617 rlb.append(STRING_WITH_LEN("START GROUP_REPLICATION"));
1618
1619 if (lex->slave_connection.user) {
1620 comma = append_str(&rlb, comma, " USER =", lex->slave_connection.user);
1621 }
1622
1623 if (lex->slave_connection.password) {
1624 comma = append_str(&rlb, comma, " PASSWORD =", "<secret>");
1625 }
1626
1627 if (lex->slave_connection.plugin_auth) {
1628 comma = append_str(&rlb, comma,
1629 " DEFAULT_AUTH =", lex->slave_connection.plugin_auth);
1630 }
1631
1632 return true;
1633 }
1634