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 &gt; n2
201   @retval 0 if n1 &lt;= 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, &current_user, &rlb);
1090     rlb.append(STRING_WITH_LEN("@"));
1091     append_query_string(m_thd, system_charset_info, &current_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