1 /* Copyright (c) 2011, 2021, Oracle and/or its affiliates.
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
22    02110-1301  USA */
23 
24 
25 /*
26   In here, we rewrite queries (to obfuscate passwords etc.) that need it
27   before we log them.
28 
29   Stored procedures may also rewrite their statements (to show the actual
30   values of their variables etc.). There is currently no scenario where
31   a statement can be eligible for both rewrites. (see sp_instr.cc)
32   Special consideration will need to be taken if this assertion is changed.
33 
34   We also do not intersect with query cache at this time, as QC only
35   caches SELECTs (which we don't rewrite). If and when QC becomes more
36   general, it should probably cache the rewritten query along with the
37   user-submitted one. (see sql_parse.cc)
38 */
39 
40 
41 #include "auth_common.h"    // append_user
42 #include "sql_parse.h"  // get_current_user
43 #include "sql_show.h"   // append_identifier
44 #include "sp_head.h"    // struct set_var_base
45 #include "rpl_slave.h"  // SLAVE_SQL, SLAVE_IO
46 #include "mysqld.h"     // opt_log_builtin_as_identified_by_password
47 #include "log.h"
48 
49 /**
50   Append a key/value pair to a string, with an optional preceding comma.
51   For numeric values.
52 
53   @param           str                  The string to append to
54   @param           comma                Prepend a comma?
55   @param           txt                  C-string, must end in a space
56   @param           len                  strlen(txt)
57   @param           val                  numeric value
58   @param           cond                 only append if this evaluates to true
59 
60   @retval          false if any subsequent key/value pair would be the first
61 */
62 
append_int(String * str,bool comma,const char * txt,size_t len,long val,int cond)63 bool append_int(String *str, bool comma, const char *txt, size_t len,
64                 long val, int cond)
65 {
66   if (cond)
67   {
68     String numbuf(42);
69     if (comma)
70       str->append(STRING_WITH_LEN(", "));
71     str->append(txt,len);
72     numbuf.set((longlong)val,&my_charset_bin);
73     str->append(numbuf);
74     return true;
75   }
76   return comma;
77 }
78 
79 
80 /**
81   Append a key/value pair to a string if the value is non-NULL,
82   with an optional preceding comma.
83 
84   @param           str                  The string to append to
85   @param           comma                Prepend a comma?
86   @param           key                  C-string: the key, must be non-NULL
87   @param           val                  C-string: the value
88 
89   @retval          false if any subsequent key/value pair would be the first
90 */
91 
append_str(String * str,bool comma,const char * key,const char * val)92 bool append_str(String *str, bool comma, const char *key, const char *val)
93 {
94   if (val)
95   {
96     if (comma)
97       str->append(STRING_WITH_LEN(", "));
98     str->append(key);
99     str->append(STRING_WITH_LEN(" '"));
100     str->append(val);
101     str->append(STRING_WITH_LEN("'"));
102     return true;
103   }
104   return comma;
105 }
106 
rewrite_ssl_properties(LEX * lex,String * rlb)107 void rewrite_ssl_properties(LEX *lex, String *rlb)
108 {
109   if (lex->ssl_type != SSL_TYPE_NOT_SPECIFIED)
110   {
111     rlb->append(STRING_WITH_LEN(" REQUIRE"));
112     switch (lex->ssl_type)
113     {
114     case SSL_TYPE_SPECIFIED:
115       if (lex->x509_subject)
116       {
117         rlb->append(STRING_WITH_LEN(" SUBJECT '"));
118         rlb->append(lex->x509_subject);
119         rlb->append(STRING_WITH_LEN("'"));
120       }
121       if (lex->x509_issuer)
122       {
123         rlb->append(STRING_WITH_LEN(" ISSUER '"));
124         rlb->append(lex->x509_issuer);
125         rlb->append(STRING_WITH_LEN("'"));
126       }
127       if (lex->ssl_cipher)
128       {
129         rlb->append(STRING_WITH_LEN(" CIPHER '"));
130         rlb->append(lex->ssl_cipher);
131         rlb->append(STRING_WITH_LEN("'"));
132       }
133       break;
134     case SSL_TYPE_X509:
135       rlb->append(STRING_WITH_LEN(" X509"));
136       break;
137     case SSL_TYPE_ANY:
138       rlb->append(STRING_WITH_LEN(" SSL"));
139       break;
140     case SSL_TYPE_NOT_SPECIFIED:
141       /* fall-thru */
142     case SSL_TYPE_NONE:
143       rlb->append(STRING_WITH_LEN(" NONE"));
144       break;
145     }
146   }
147 }
148 
rewrite_user_resources(LEX * lex,String * rlb)149 void rewrite_user_resources(LEX *lex, String *rlb)
150 {
151   if (lex->mqh.specified_limits || (lex->grant & GRANT_ACL))
152   {
153     rlb->append(STRING_WITH_LEN(" WITH"));
154     if (lex->grant & GRANT_ACL)
155       rlb->append(STRING_WITH_LEN(" GRANT OPTION"));
156 
157     append_int(rlb, false, STRING_WITH_LEN(" MAX_QUERIES_PER_HOUR "),
158                lex->mqh.questions,
159                lex->mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR);
160 
161     append_int(rlb, false, STRING_WITH_LEN(" MAX_UPDATES_PER_HOUR "),
162                lex->mqh.updates,
163                lex->mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR);
164 
165     append_int(rlb, false, STRING_WITH_LEN(" MAX_CONNECTIONS_PER_HOUR "),
166                lex->mqh.conn_per_hour,
167                lex->mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR);
168 
169     append_int(rlb, false, STRING_WITH_LEN(" MAX_USER_CONNECTIONS "),
170                lex->mqh.user_conn,
171                lex->mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS);
172   }
173 }
174 
rewrite_account_lock(LEX * lex,String * rlb)175 void rewrite_account_lock(LEX *lex, String *rlb)
176 {
177   if (lex->alter_password.account_locked)
178   {
179     rlb->append(STRING_WITH_LEN(" ACCOUNT LOCK"));
180   }
181   else
182   {
183     rlb->append(STRING_WITH_LEN(" ACCOUNT UNLOCK"));
184   }
185 }
186 
187 /**
188   Rewrite a GRANT statement.
189 
190   @param thd      The THD to rewrite for.
191   @param rlb      An empty String object to put the rewritten query in.
192 */
193 
mysql_rewrite_grant(THD * thd,String * rlb)194 void mysql_rewrite_grant(THD *thd, String *rlb)
195 {
196   LEX        *lex= thd->lex;
197   TABLE_LIST *first_table= lex->select_lex->table_list.first;
198   bool        comma= FALSE, comma_inner;
199   bool        proxy_grant= lex->type == TYPE_ENUM_PROXY;
200   String      cols(1024);
201   int         c;
202 
203   rlb->append(STRING_WITH_LEN("GRANT "));
204 
205   if (proxy_grant)
206     rlb->append(STRING_WITH_LEN("PROXY"));
207   else if (lex->all_privileges)
208     rlb->append(STRING_WITH_LEN("ALL PRIVILEGES"));
209   else
210   {
211     ulong priv;
212 
213     for (c= 0, priv= SELECT_ACL; priv <= GLOBAL_ACLS; c++, priv <<= 1)
214     {
215       if (priv == GRANT_ACL)
216         continue;
217 
218       comma_inner= FALSE;
219 
220       if (lex->columns.elements)               // show columns, if any
221       {
222         class LEX_COLUMN *column;
223         List_iterator <LEX_COLUMN> column_iter(lex->columns);
224 
225         cols.length(0);
226         cols.append(STRING_WITH_LEN(" ("));
227 
228         /*
229           If the statement was GRANT SELECT(f2), INSERT(f3), UPDATE(f1,f3, f2),
230           our list cols will contain the order f2, f3, f1, and thus that's
231           the order we'll recreate the privilege: UPDATE (f2, f3, f1)
232         */
233 
234         while ((column= column_iter++))
235         {
236           if (column->rights & priv)
237           {
238             if (comma_inner)
239               cols.append(STRING_WITH_LEN(", "));
240             else
241               comma_inner= TRUE;
242             append_identifier(thd, &cols, column->column.ptr(), column->column.length());
243           }
244         }
245         cols.append(STRING_WITH_LEN(")"));
246       }
247 
248       if (comma_inner || (lex->grant & priv))  // show privilege name
249       {
250         if (comma)
251           rlb->append(STRING_WITH_LEN(", "));
252         else
253           comma= TRUE;
254         rlb->append(command_array[c],command_lengths[c]);
255         if (!(lex->grant & priv))              // general outranks specific
256           rlb->append(cols);
257       }
258     }
259     if (!comma)                                // no privs, default to USAGE
260       rlb->append(STRING_WITH_LEN("USAGE"));
261   }
262 
263   rlb->append(STRING_WITH_LEN(" ON "));
264   switch(lex->type)
265   {
266     case TYPE_ENUM_PROCEDURE: rlb->append(STRING_WITH_LEN("PROCEDURE ")); break;
267     case TYPE_ENUM_FUNCTION:  rlb->append(STRING_WITH_LEN("FUNCTION "));  break;
268     default:                                                              break;
269   }
270 
271   LEX_USER *user_name, *tmp_user_name;
272   List_iterator <LEX_USER> user_list(lex->users_list);
273   comma= FALSE;
274 
275   if (proxy_grant)
276   {
277     tmp_user_name= user_list++;
278     user_name= get_current_user(thd, tmp_user_name);
279     if (user_name)
280       append_user_new(thd, rlb, user_name, comma);
281   }
282   else if (first_table)
283   {
284     if (first_table->is_view())
285     {
286       append_identifier(thd, rlb, first_table->view_db.str,
287                         first_table->view_db.length);
288       rlb->append(STRING_WITH_LEN("."));
289       append_identifier(thd, rlb, first_table->view_name.str,
290                         first_table->view_name.length);
291     }
292     else
293     {
294       append_identifier(thd, rlb, first_table->db, strlen(first_table->db));
295       rlb->append(STRING_WITH_LEN("."));
296       append_identifier(thd, rlb, first_table->table_name,
297                         strlen(first_table->table_name));
298     }
299   }
300   else
301   {
302     if (lex->current_select()->db)
303       append_identifier(thd, rlb, lex->current_select()->db,
304                         strlen(lex->current_select()->db));
305     else
306       rlb->append("*");
307     rlb->append(STRING_WITH_LEN(".*"));
308   }
309 
310   rlb->append(STRING_WITH_LEN(" TO "));
311   {
312     while ((tmp_user_name= user_list++))
313     {
314       if ((user_name= get_current_user(thd, tmp_user_name)))
315       {
316         if (opt_log_builtin_as_identified_by_password)
317           append_user(thd, rlb, user_name, comma, true);
318         else
319           append_user_new(thd, rlb, user_name, comma);
320         comma= TRUE;
321       }
322     }
323   }
324   rewrite_ssl_properties(lex, rlb);
325   rewrite_user_resources(lex, rlb);
326 }
327 
328 
329 /**
330   Rewrite a SET statement.
331 
332   @param thd      The THD to rewrite for.
333   @param rlb      An empty String object to put the rewritten query in.
334 */
335 
mysql_rewrite_set(THD * thd,String * rlb)336 static void mysql_rewrite_set(THD *thd, String *rlb)
337 {
338   LEX                              *lex= thd->lex;
339   List_iterator_fast<set_var_base>  it(lex->var_list);
340   set_var_base                     *var;
341   bool                              comma= FALSE;
342 
343   rlb->append(STRING_WITH_LEN("SET "));
344 
345   while ((var= it++))
346   {
347     if (comma)
348       rlb->append(STRING_WITH_LEN(","));
349     else
350       comma= TRUE;
351 
352     var->print(thd, rlb);
353   }
354 }
355 
356 /**
357   Rewrite CREATE/ALTER USER statement.
358 
359   @param thd                     The THD to rewrite for.
360   @param rlb                     An empty String object to put the rewritten query in.
361   @param hide_password_hash      If password hash has to be shown as <secret> or not.
362 */
363 
mysql_rewrite_create_alter_user(THD * thd,String * rlb,std::set<LEX_USER * > * extra_users,bool hide_password_hash)364 void mysql_rewrite_create_alter_user(THD *thd, String *rlb,
365                                      std::set<LEX_USER *> *extra_users,
366                                      bool hide_password_hash)
367 {
368   LEX                      *lex= thd->lex;
369   LEX_USER                 *user_name, *tmp_user_name;
370   List_iterator <LEX_USER>  user_list(lex->users_list);
371   bool                      comma= FALSE;
372 
373   if (thd->lex->sql_command == SQLCOM_CREATE_USER ||
374       thd->lex->sql_command == SQLCOM_SHOW_CREATE_USER)
375     rlb->append(STRING_WITH_LEN("CREATE USER "));
376   else
377     rlb->append(STRING_WITH_LEN("ALTER USER "));
378 
379   if (thd->lex->sql_command == SQLCOM_CREATE_USER &&
380       thd->lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS)
381     rlb->append(STRING_WITH_LEN("IF NOT EXISTS "));
382   if (thd->lex->sql_command == SQLCOM_ALTER_USER &&
383       thd->lex->drop_if_exists)
384     rlb->append(STRING_WITH_LEN("IF EXISTS "));
385 
386   while ((tmp_user_name= user_list++))
387   {
388     if ((user_name= get_current_user(thd, tmp_user_name)))
389     {
390       if (opt_log_builtin_as_identified_by_password &&
391           thd->lex->sql_command != SQLCOM_ALTER_USER)
392         append_user(thd, rlb, user_name, comma, true);
393       else
394         append_user_new(thd, rlb, user_name, comma, hide_password_hash);
395       comma= TRUE;
396     }
397   }
398 
399   rewrite_ssl_properties(lex, rlb);
400   rewrite_user_resources(lex, rlb);
401 
402   /* rewrite password expired */
403   if (lex->alter_password.update_password_expired_fields)
404   {
405     if (lex->alter_password.update_password_expired_column)
406     {
407       rlb->append(STRING_WITH_LEN(" PASSWORD EXPIRE"));
408     }
409     else if (lex->alter_password.expire_after_days)
410     {
411       append_int(rlb, false, STRING_WITH_LEN(" PASSWORD EXPIRE INTERVAL "),
412                  lex->alter_password.expire_after_days, TRUE);
413       rlb->append(STRING_WITH_LEN(" DAY"));
414     }
415     else if (lex->alter_password.use_default_password_lifetime)
416     {
417       rlb->append(STRING_WITH_LEN(" PASSWORD EXPIRE DEFAULT"));
418     }
419     else
420     {
421       rlb->append(STRING_WITH_LEN(" PASSWORD EXPIRE NEVER"));
422     }
423   }
424 
425   if (lex->alter_password.update_account_locked_column)
426   {
427     rewrite_account_lock(lex, rlb);
428   }
429 
430   if ((lex->sql_command == SQLCOM_CREATE_USER ||
431       lex->sql_command == SQLCOM_ALTER_USER) &&
432       extra_users && extra_users->size())
433   {
434     String warn_user;
435     bool comma= false;
436     bool log_warning= false;
437     std::set<LEX_USER *>::iterator it;
438     for (it = extra_users->begin(); it != extra_users->end(); it++)
439     {
440       /*
441         Consider for warning if one of the following is true:
442         1. If SQLCOM_CREATE_USER and IF NOT EXISTS clause is used and
443            IDENTIFIED WITH clause is not used
444         2. If SQLCOM_ALTER_USER and IF EXISTS clause is used and
445            IDENTIFIED WITH clause is not used
446       */
447       LEX_USER* extra_user= *it;
448       if (!extra_user->uses_identified_with_clause &&
449           (lex->sql_command == SQLCOM_CREATE_USER ||
450           extra_user->uses_identified_by_clause))
451       {
452         append_user(thd, &warn_user, extra_user, comma, false);
453         comma= true;
454         log_warning= true;
455       }
456     }
457     if (log_warning)
458     {
459       if (lex->sql_command == SQLCOM_CREATE_USER)
460       {
461         sql_print_warning("Following users were specified in CREATE USER "
462                           "IF NOT EXISTS but they already exist. "
463                           "Corresponding entry in binary log used default "
464                           "authentication plugin '%s' to rewrite "
465                           "authentication information(if any) for them: %s\n",
466                           default_auth_plugin, warn_user.c_ptr_safe());
467       }
468       else if (lex->sql_command == SQLCOM_ALTER_USER)
469       {
470         sql_print_warning("Following users were specified in ALTER USER "
471                           "IF EXISTS but they do not exist. "
472                           "Corresponding entry in binary log used default "
473                           "authentication plugin '%s' to rewrite "
474                           "authentication information(if any) for them: %s\n",
475                           default_auth_plugin, warn_user.c_ptr_safe());
476       }
477     }
478     warn_user.mem_free();
479   }
480 }
481 
482 /**
483   Rewrite a CHANGE MASTER statement.
484 
485   @param thd      The THD to rewrite for.
486   @param rlb      An empty String object to put the rewritten query in.
487 */
488 
mysql_rewrite_change_master(THD * thd,String * rlb)489 static void mysql_rewrite_change_master(THD *thd, String *rlb)
490 {
491   LEX *lex= thd->lex;
492 
493   rlb->append(STRING_WITH_LEN("CHANGE MASTER TO"));
494 
495   if (lex->mi.host)
496   {
497     rlb->append(STRING_WITH_LEN(" MASTER_HOST = '"));
498     rlb->append(lex->mi.host);
499     rlb->append(STRING_WITH_LEN("'"));
500   }
501   if (lex->mi.user)
502   {
503     rlb->append(STRING_WITH_LEN(" MASTER_USER = '"));
504     rlb->append(lex->mi.user);
505     rlb->append(STRING_WITH_LEN("'"));
506   }
507   if (lex->mi.password)
508   {
509     rlb->append(STRING_WITH_LEN(" MASTER_PASSWORD = <secret>"));
510   }
511   if (lex->mi.port)
512   {
513     rlb->append(STRING_WITH_LEN(" MASTER_PORT = "));
514     rlb->append_ulonglong(lex->mi.port);
515   }
516   if (lex->mi.connect_retry)
517   {
518     rlb->append(STRING_WITH_LEN(" MASTER_CONNECT_RETRY = "));
519     rlb->append_ulonglong(lex->mi.connect_retry);
520   }
521   if (lex->mi.ssl)
522   {
523     rlb->append(STRING_WITH_LEN(" MASTER_SSL = "));
524     rlb->append(lex->mi.ssl == LEX_MASTER_INFO::LEX_MI_ENABLE ? "1" : "0");
525   }
526   if (lex->mi.ssl_ca)
527   {
528     rlb->append(STRING_WITH_LEN(" MASTER_SSL_CA = '"));
529     rlb->append(lex->mi.ssl_ca);
530     rlb->append(STRING_WITH_LEN("'"));
531   }
532   if (lex->mi.ssl_capath)
533   {
534     rlb->append(STRING_WITH_LEN(" MASTER_SSL_CAPATH = '"));
535     rlb->append(lex->mi.ssl_capath);
536     rlb->append(STRING_WITH_LEN("'"));
537   }
538 
539   if (lex->mi.tls_version)
540   {
541     rlb->append(STRING_WITH_LEN(" MASTER_TLS_VERSION = '"));
542     rlb->append(lex->mi.tls_version);
543     rlb->append(STRING_WITH_LEN("'"));
544   }
545 
546   if (lex->mi.ssl_cert)
547   {
548     rlb->append(STRING_WITH_LEN(" MASTER_SSL_CERT = '"));
549     rlb->append(lex->mi.ssl_cert);
550     rlb->append(STRING_WITH_LEN("'"));
551   }
552   if (lex->mi.ssl_cipher)
553   {
554     rlb->append(STRING_WITH_LEN(" MASTER_SSL_CIPHER = '"));
555     rlb->append(lex->mi.ssl_cipher);
556     rlb->append(STRING_WITH_LEN("'"));
557   }
558   if (lex->mi.ssl_key)
559   {
560     rlb->append(STRING_WITH_LEN(" MASTER_SSL_KEY = '"));
561     rlb->append(lex->mi.ssl_key);
562     rlb->append(STRING_WITH_LEN("'"));
563   }
564   if (lex->mi.log_file_name)
565   {
566     rlb->append(STRING_WITH_LEN(" MASTER_LOG_FILE = '"));
567     rlb->append(lex->mi.log_file_name);
568     rlb->append(STRING_WITH_LEN("'"));
569   }
570   if (lex->mi.pos)
571   {
572     rlb->append(STRING_WITH_LEN(" MASTER_LOG_POS = "));
573     rlb->append_ulonglong(lex->mi.pos);
574   }
575   if (lex->mi.relay_log_name)
576   {
577     rlb->append(STRING_WITH_LEN(" RELAY_LOG_FILE = '"));
578     rlb->append(lex->mi.relay_log_name);
579     rlb->append(STRING_WITH_LEN("'"));
580   }
581   if (lex->mi.relay_log_pos)
582   {
583     rlb->append(STRING_WITH_LEN(" RELAY_LOG_POS = "));
584     rlb->append_ulonglong(lex->mi.relay_log_pos);
585   }
586 
587   if (lex->mi.ssl_verify_server_cert)
588   {
589     rlb->append(STRING_WITH_LEN(" MASTER_SSL_VERIFY_SERVER_CERT = "));
590     rlb->append(lex->mi.ssl_verify_server_cert == LEX_MASTER_INFO::LEX_MI_ENABLE ? "1" : "0");
591   }
592   if (lex->mi.repl_ignore_server_ids_opt)
593   {
594     bool first= TRUE;
595     rlb->append(STRING_WITH_LEN(" IGNORE_SERVER_IDS = ( "));
596     for (size_t i= 0; i < lex->mi.repl_ignore_server_ids.size(); i++)
597     {
598       ulong s_id= lex->mi.repl_ignore_server_ids[i];
599       if (first)
600         first= FALSE;
601       else
602         rlb->append(STRING_WITH_LEN(", "));
603       rlb->append_ulonglong(s_id);
604     }
605     rlb->append(STRING_WITH_LEN(" )"));
606   }
607   if (lex->mi.heartbeat_opt != LEX_MASTER_INFO::LEX_MI_UNCHANGED)
608   {
609     rlb->append(STRING_WITH_LEN(" MASTER_HEARTBEAT_PERIOD = "));
610     if (lex->mi.heartbeat_opt == LEX_MASTER_INFO::LEX_MI_DISABLE)
611       rlb->append(STRING_WITH_LEN("0"));
612     else
613     {
614       char buf[64];
615       my_snprintf(buf, 64, "%f", lex->mi.heartbeat_period);
616       rlb->append(buf);
617     }
618   }
619 }
620 
621 
622 /**
623   Rewrite a START SLAVE statement.
624 
625   @param thd      The THD to rewrite for.
626   @param rlb      An empty String object to put the rewritten query in.
627 */
628 
mysql_rewrite_start_slave(THD * thd,String * rlb)629 static void mysql_rewrite_start_slave(THD *thd, String *rlb)
630 {
631   LEX *lex= thd->lex;
632 
633   if (!lex->slave_connection.password)
634     return;
635 
636   rlb->append(STRING_WITH_LEN("START SLAVE"));
637 
638   if (lex->slave_thd_opt & SLAVE_IO)
639     rlb->append(STRING_WITH_LEN(" IO_THREAD"));
640 
641   /* we have printed the IO THREAD related options */
642   if (lex->slave_thd_opt & SLAVE_IO &&
643       lex->slave_thd_opt & SLAVE_SQL)
644     rlb->append(STRING_WITH_LEN(","));
645 
646   if (lex->slave_thd_opt & SLAVE_SQL)
647     rlb->append(STRING_WITH_LEN(" SQL_THREAD"));
648 
649   /* until options */
650   if (lex->mi.log_file_name || lex->mi.relay_log_name)
651   {
652     rlb->append(STRING_WITH_LEN(" UNTIL"));
653     if (lex->mi.log_file_name)
654     {
655       rlb->append(STRING_WITH_LEN(" MASTER_LOG_FILE = '"));
656       rlb->append(lex->mi.log_file_name);
657       rlb->append(STRING_WITH_LEN("', "));
658       rlb->append(STRING_WITH_LEN("MASTER_LOG_POS = "));
659       rlb->append_ulonglong(lex->mi.pos);
660     }
661 
662     if (lex->mi.relay_log_name)
663     {
664       rlb->append(STRING_WITH_LEN(" RELAY_LOG_FILE = '"));
665       rlb->append(lex->mi.relay_log_name);
666       rlb->append(STRING_WITH_LEN("', "));
667       rlb->append(STRING_WITH_LEN("RELAY_LOG_POS = "));
668       rlb->append_ulonglong(lex->mi.relay_log_pos);
669     }
670   }
671 
672   /* connection options */
673   if (lex->slave_connection.user)
674   {
675     rlb->append(STRING_WITH_LEN(" USER = '"));
676     rlb->append(lex->slave_connection.user);
677     rlb->append(STRING_WITH_LEN("'"));
678   }
679 
680   if (lex->slave_connection.password)
681     rlb->append(STRING_WITH_LEN(" PASSWORD = '<secret>'"));
682 
683   if (lex->slave_connection.plugin_auth)
684   {
685     rlb->append(STRING_WITH_LEN(" DEFAULT_AUTH = '"));
686     rlb->append(lex->slave_connection.plugin_auth);
687     rlb->append(STRING_WITH_LEN("'"));
688   }
689 
690   if (lex->slave_connection.plugin_dir)
691   {
692     rlb->append(STRING_WITH_LEN(" PLUGIN_DIR = '"));
693     rlb->append(lex->slave_connection.plugin_dir);
694     rlb->append(STRING_WITH_LEN("'"));
695   }
696 }
697 
698 
699 /**
700   Rewrite a SERVER OPTIONS clause (for CREATE SERVER and ALTER SERVER).
701 
702   @param thd      The THD to rewrite for.
703   @param rlb      An empty String object to put the rewritten query in.
704 */
705 
mysql_rewrite_server_options(THD * thd,String * rlb)706 static void mysql_rewrite_server_options(THD *thd, String *rlb)
707 {
708   LEX *lex= thd->lex;
709 
710   rlb->append(STRING_WITH_LEN(" OPTIONS ( "));
711 
712   rlb->append(STRING_WITH_LEN("PASSWORD '<secret>'"));
713   append_str(rlb, true, "USER", lex->server_options.get_username());
714   append_str(rlb, true, "HOST", lex->server_options.get_host());
715   append_str(rlb, true, "DATABASE", lex->server_options.get_db());
716   append_str(rlb, true, "OWNER", lex->server_options.get_owner());
717   append_str(rlb, true, "SOCKET", lex->server_options.get_socket());
718   append_int(rlb, true, STRING_WITH_LEN("PORT "),
719              lex->server_options.get_port(),
720              lex->server_options.get_port() != Server_options::PORT_NOT_SET);
721 
722   rlb->append(STRING_WITH_LEN(" )"));
723 }
724 
725 
726 /**
727   Rewrite a CREATE SERVER statement.
728 
729   @param thd      The THD to rewrite for.
730   @param rlb      An empty String object to put the rewritten query in.
731 */
732 
mysql_rewrite_create_server(THD * thd,String * rlb)733 static void mysql_rewrite_create_server(THD *thd, String *rlb)
734 {
735   LEX *lex= thd->lex;
736 
737   if (!lex->server_options.get_password())
738     return;
739 
740   rlb->append(STRING_WITH_LEN("CREATE SERVER "));
741 
742   rlb->append(lex->server_options.m_server_name.str ?
743               lex->server_options.m_server_name.str : "");
744 
745   rlb->append(STRING_WITH_LEN(" FOREIGN DATA WRAPPER '"));
746   rlb->append(lex->server_options.get_scheme() ?
747               lex->server_options.get_scheme() : "");
748   rlb->append(STRING_WITH_LEN("'"));
749 
750   mysql_rewrite_server_options(thd, rlb);
751 }
752 
753 
754 /**
755   Rewrite a ALTER SERVER statement.
756 
757   @param thd      The THD to rewrite for.
758   @param rlb      An empty String object to put the rewritten query in.
759 */
760 
mysql_rewrite_alter_server(THD * thd,String * rlb)761 static void mysql_rewrite_alter_server(THD *thd, String *rlb)
762 {
763   LEX *lex= thd->lex;
764 
765   if (!lex->server_options.get_password())
766     return;
767 
768   rlb->append(STRING_WITH_LEN("ALTER SERVER "));
769 
770   rlb->append(lex->server_options.m_server_name.str ?
771               lex->server_options.m_server_name.str : "");
772 
773   mysql_rewrite_server_options(thd, rlb);
774 }
775 
776 
777 
778 
779 /**
780   Rewrite a PREPARE statement.
781 
782   @param thd      The THD to rewrite for.
783   @param rlb      An empty String object to put the rewritten query in.
784 */
785 
mysql_rewrite_prepare(THD * thd,String * rlb)786 static void mysql_rewrite_prepare(THD *thd, String *rlb)
787 {
788   LEX *lex= thd->lex;
789 
790   if (lex->prepared_stmt_code_is_varref)
791     return;
792 
793   rlb->append(STRING_WITH_LEN("PREPARE "));
794   rlb->append(lex->prepared_stmt_name.str,
795               lex->prepared_stmt_name.length);
796   rlb->append(STRING_WITH_LEN(" FROM ..."));
797 }
798 
799 
800 
801 
802 /**
803    Rewrite a query (to obfuscate passwords etc.)
804 
805    Side-effects:
806 
807    - thd->m_rewritten_query will contain a rewritten query,
808      or be cleared if no rewriting took place.
809      LOCK_thd_query will be temporarily acquired to make that change.
810 
811    @note Keep in mind that these side-effects will only happen when
812          calling this top-level function, but not when calling
813          individual sub-functions directly!
814 
815    @param thd     The THD to rewrite for.
816 */
817 
mysql_rewrite_query(THD * thd)818 void mysql_rewrite_query(THD *thd)
819 {
820   String rlb;
821 
822   // We should not come through here twice for the same query.
823   assert(thd->rewritten_query().length() == 0);
824 
825   if (thd->lex->contains_plaintext_password)
826   {
827     switch(thd->lex->sql_command)
828     {
829     case SQLCOM_GRANT:         mysql_rewrite_grant(thd, &rlb);         break;
830     case SQLCOM_SET_OPTION:    mysql_rewrite_set(thd, &rlb);           break;
831     case SQLCOM_CREATE_USER:
832     case SQLCOM_ALTER_USER:
833                         mysql_rewrite_create_alter_user(thd, &rlb);    break;
834     case SQLCOM_CHANGE_MASTER: mysql_rewrite_change_master(thd, &rlb); break;
835     case SQLCOM_SLAVE_START:   mysql_rewrite_start_slave(thd, &rlb);   break;
836     case SQLCOM_CREATE_SERVER: mysql_rewrite_create_server(thd, &rlb); break;
837     case SQLCOM_ALTER_SERVER:  mysql_rewrite_alter_server(thd, &rlb);  break;
838 
839     /*
840       PREPARE stmt FROM <string> is rewritten so that <string> is
841       not logged.  The statement in <string> will in turn be logged
842       by the prepare and the execute functions in sql_prepare.cc.
843       They do call rewrite so they can safely log the statement,
844       but when they call us, it'll be with sql_command set to reflect
845       the statement in question, not SQLCOM_PREPARE or SQLCOM_EXECUTE.
846       Therefore, there is no SQLCOM_EXECUTE case here, and all
847       SQLCOM_PREPARE does is remove <string>; the "other half",
848       i.e. printing what string we prepare from happens when the
849       prepare function calls the logger (and comes by here with
850       sql_command set to the command being prepared).
851     */
852     case SQLCOM_PREPARE:       mysql_rewrite_prepare(thd, &rlb);       break;
853     default:                   /* unhandled query types are legal. */  break;
854     }
855   }
856 
857   // Note that we succeeded in rewriting (where applicable).
858   if (rlb.length() > 0)
859     thd->swap_rewritten_query(rlb);
860 }
861