MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sql_rewrite.cc
1 /* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or
4  modify it under the terms of the GNU General Public License
5  as published by the Free Software Foundation; version 2 of
6  the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
16  02110-1301 USA */
17 
18 
19 /*
20  In here, we rewrite queries (to obfuscate passwords etc.) that need it
21  before we log them.
22 
23  Stored procedures may also rewrite their statements (to show the actual
24  values of their variables etc.). There is currently no scenario where
25  a statement can be eligible for both rewrites. (see sp_instr.cc)
26  Special consideration will need to be taken if this assertion is changed.
27 
28  We also do not intersect with query cache at this time, as QC only
29  caches SELECTs (which we don't rewrite). If and when QC becomes more
30  general, it should probably cache the rewritten query along with the
31  user-submitted one. (see sql_parse.cc)
32 */
33 
34 
35 #include "sql_acl.h" // append_user
36 #include "sql_parse.h" // get_current_user
37 #include "sql_show.h" // append_identifier
38 #include "sp_head.h" // struct set_var_base
39 #include "rpl_slave.h" // SLAVE_SQL, SLAVE_IO
40 
41 
56 bool append_int(String *str, bool comma, const char *txt, size_t len,
57  long val, int cond)
58 {
59  if (cond)
60  {
61  String numbuf(42);
62  if (comma)
63  str->append(STRING_WITH_LEN(", "));
64  str->append(txt,len);
65  numbuf.set((longlong)val,&my_charset_bin);
66  str->append(numbuf);
67  return true;
68  }
69  return comma;
70 }
71 
72 
85 bool append_str(String *str, bool comma, const char *key, char *val)
86 {
87  if (val)
88  {
89  if (comma)
90  str->append(STRING_WITH_LEN(", "));
91  str->append(key);
92  str->append(STRING_WITH_LEN(" '"));
93  str->append(val);
94  str->append(STRING_WITH_LEN("'"));
95  return true;
96  }
97  return comma;
98 }
99 
100 
108 static void mysql_rewrite_grant(THD *thd, String *rlb)
109 {
110  LEX *lex= thd->lex;
111  TABLE_LIST *first_table= (TABLE_LIST*) lex->select_lex.table_list.first;
112  bool comma= FALSE, comma_inner;
113  String cols(1024);
114  int c;
115 
116  rlb->append(STRING_WITH_LEN("GRANT "));
117 
118  if (lex->all_privileges)
119  rlb->append(STRING_WITH_LEN("ALL PRIVILEGES"));
120  else
121  {
122  ulong priv;
123 
124  for (c= 0, priv= SELECT_ACL; priv <= GLOBAL_ACLS; c++, priv <<= 1)
125  {
126  if (priv == GRANT_ACL)
127  continue;
128 
129  comma_inner= FALSE;
130 
131  if (lex->columns.elements) // show columns, if any
132  {
133  class LEX_COLUMN *column;
134  List_iterator <LEX_COLUMN> column_iter(lex->columns);
135 
136  cols.length(0);
137  cols.append(STRING_WITH_LEN(" ("));
138 
139  /*
140  If the statement was GRANT SELECT(f2), INSERT(f3), UPDATE(f1,f3, f2),
141  our list cols will contain the order f2, f3, f1, and thus that's
142  the order we'll recreate the privilege: UPDATE (f2, f3, f1)
143  */
144 
145  while ((column= column_iter++))
146  {
147  if (column->rights & priv)
148  {
149  if (comma_inner)
150  cols.append(STRING_WITH_LEN(", "));
151  else
152  comma_inner= TRUE;
153  cols.append(column->column.ptr(),column->column.length());
154  }
155  }
156  cols.append(STRING_WITH_LEN(")"));
157  }
158 
159  if (comma_inner || (lex->grant & priv)) // show privilege name
160  {
161  if (comma)
162  rlb->append(STRING_WITH_LEN(", "));
163  else
164  comma= TRUE;
165  rlb->append(command_array[c],command_lengths[c]);
166  if (!(lex->grant & priv)) // general outranks specific
167  rlb->append(cols);
168  }
169  }
170  if (!comma) // no privs, default to USAGE
171  rlb->append(STRING_WITH_LEN("USAGE"));
172  }
173 
174  rlb->append(STRING_WITH_LEN(" ON "));
175  switch(lex->type)
176  {
177  case TYPE_ENUM_PROCEDURE: rlb->append(STRING_WITH_LEN("PROCEDURE ")); break;
178  case TYPE_ENUM_FUNCTION: rlb->append(STRING_WITH_LEN("FUNCTION ")); break;
179  default: break;
180  }
181 
182  if (first_table)
183  {
184  append_identifier(thd, rlb, first_table->db, strlen(first_table->db));
185  rlb->append(STRING_WITH_LEN("."));
186  append_identifier(thd, rlb, first_table->table_name,
187  strlen(first_table->table_name));
188  }
189  else
190  {
191  if (lex->current_select->db)
192  append_identifier(thd, rlb, lex->current_select->db,
193  strlen(lex->current_select->db));
194  else
195  rlb->append("*");
196  rlb->append(STRING_WITH_LEN(".*"));
197  }
198 
199  rlb->append(STRING_WITH_LEN(" TO "));
200  {
201  LEX_USER *user_name, *tmp_user_name;
202  List_iterator <LEX_USER> user_list(lex->users_list);
203  bool comma= FALSE;
204 
205  while ((tmp_user_name= user_list++))
206  {
207  if ((user_name= get_current_user(thd, tmp_user_name)))
208  {
209  append_user(thd, rlb, user_name, comma, true);
210  comma= TRUE;
211  }
212  }
213  }
214 
215  if (lex->ssl_type != SSL_TYPE_NOT_SPECIFIED)
216  {
217  rlb->append(STRING_WITH_LEN(" REQUIRE"));
218  switch (lex->ssl_type)
219  {
220  case SSL_TYPE_SPECIFIED:
221  if (lex->x509_subject)
222  {
223  rlb->append(STRING_WITH_LEN(" SUBJECT '"));
224  rlb->append(lex->x509_subject);
225  rlb->append(STRING_WITH_LEN("'"));
226  }
227  if (lex->x509_issuer)
228  {
229  rlb->append(STRING_WITH_LEN(" ISSUER '"));
230  rlb->append(lex->x509_issuer);
231  rlb->append(STRING_WITH_LEN("'"));
232  }
233  if (lex->ssl_cipher)
234  {
235  rlb->append(STRING_WITH_LEN(" CIPHER '"));
236  rlb->append(lex->ssl_cipher);
237  rlb->append(STRING_WITH_LEN("'"));
238  }
239  break;
240  case SSL_TYPE_X509:
241  rlb->append(STRING_WITH_LEN(" X509"));
242  break;
243  case SSL_TYPE_ANY:
244  rlb->append(STRING_WITH_LEN(" SSL"));
245  break;
246  case SSL_TYPE_NOT_SPECIFIED:
247  /* fall-thru */
248  case SSL_TYPE_NONE:
249  rlb->append(STRING_WITH_LEN(" NONE"));
250  break;
251  }
252  }
253 
254  if (lex->mqh.specified_limits || (lex->grant & GRANT_ACL))
255  {
256  rlb->append(STRING_WITH_LEN(" WITH"));
257  if (lex->grant & GRANT_ACL)
258  rlb->append(STRING_WITH_LEN(" GRANT OPTION"));
259 
260  append_int(rlb, false, STRING_WITH_LEN(" MAX_QUERIES_PER_HOUR "),
261  lex->mqh.questions,
262  lex->mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR);
263 
264  append_int(rlb, false, STRING_WITH_LEN(" MAX_UPDATES_PER_HOUR "),
265  lex->mqh.updates,
266  lex->mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR);
267 
268  append_int(rlb, false, STRING_WITH_LEN(" MAX_CONNECTIONS_PER_HOUR "),
269  lex->mqh.conn_per_hour,
270  lex->mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR);
271 
272  append_int(rlb, false, STRING_WITH_LEN(" MAX_USER_CONNECTIONS "),
273  lex->mqh.user_conn,
274  lex->mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS);
275  }
276 }
277 
278 
286 static void mysql_rewrite_set(THD *thd, String *rlb)
287 {
288  LEX *lex= thd->lex;
289  List_iterator_fast<set_var_base> it(lex->var_list);
290  set_var_base *var;
291  bool comma= FALSE;
292 
293  rlb->append(STRING_WITH_LEN("SET "));
294 
295  while ((var= it++))
296  {
297  if (comma)
298  rlb->append(STRING_WITH_LEN(","));
299  else
300  comma= TRUE;
301 
302  var->print(thd, rlb);
303  }
304 }
305 
306 
314 static void mysql_rewrite_create_user(THD *thd, String *rlb)
315 {
316  LEX *lex= thd->lex;
317  LEX_USER *user_name, *tmp_user_name;
318  List_iterator <LEX_USER> user_list(lex->users_list);
319  bool comma= FALSE;
320 
321  rlb->append(STRING_WITH_LEN("CREATE USER "));
322  while ((tmp_user_name= user_list++))
323  {
324  if ((user_name= get_current_user(thd, tmp_user_name)))
325  {
326  append_user(thd, rlb, user_name, comma, TRUE);
327  comma= TRUE;
328  }
329  }
330 }
331 
332 
340 static void mysql_rewrite_change_master(THD *thd, String *rlb)
341 {
342  LEX *lex= thd->lex;
343 
344  rlb->append(STRING_WITH_LEN("CHANGE MASTER TO"));
345 
346  if (lex->mi.host)
347  {
348  rlb->append(STRING_WITH_LEN(" MASTER_HOST = '"));
349  rlb->append(lex->mi.host);
350  rlb->append(STRING_WITH_LEN("'"));
351  }
352  if (lex->mi.user)
353  {
354  rlb->append(STRING_WITH_LEN(" MASTER_USER = '"));
355  rlb->append(lex->mi.user);
356  rlb->append(STRING_WITH_LEN("'"));
357  }
358  if (lex->mi.password)
359  {
360  rlb->append(STRING_WITH_LEN(" MASTER_PASSWORD = <secret>"));
361  }
362  if (lex->mi.port)
363  {
364  rlb->append(STRING_WITH_LEN(" MASTER_PORT = "));
365  rlb->append_ulonglong(lex->mi.port);
366  }
367  if (lex->mi.connect_retry)
368  {
369  rlb->append(STRING_WITH_LEN(" MASTER_CONNECT_RETRY = "));
370  rlb->append_ulonglong(lex->mi.connect_retry);
371  }
372  if (lex->mi.ssl)
373  {
374  rlb->append(STRING_WITH_LEN(" MASTER_SSL = "));
375  rlb->append(lex->mi.ssl == LEX_MASTER_INFO::LEX_MI_ENABLE ? "1" : "0");
376  }
377  if (lex->mi.ssl_ca)
378  {
379  rlb->append(STRING_WITH_LEN(" MASTER_SSL_CA = '"));
380  rlb->append(lex->mi.ssl_ca);
381  rlb->append(STRING_WITH_LEN("'"));
382  }
383  if (lex->mi.ssl_capath)
384  {
385  rlb->append(STRING_WITH_LEN(" MASTER_SSL_CAPATH = '"));
386  rlb->append(lex->mi.ssl_capath);
387  rlb->append(STRING_WITH_LEN("'"));
388  }
389  if (lex->mi.ssl_cert)
390  {
391  rlb->append(STRING_WITH_LEN(" MASTER_SSL_CERT = '"));
392  rlb->append(lex->mi.ssl_cert);
393  rlb->append(STRING_WITH_LEN("'"));
394  }
395  if (lex->mi.ssl_cipher)
396  {
397  rlb->append(STRING_WITH_LEN(" MASTER_SSL_CIPHER = '"));
398  rlb->append(lex->mi.ssl_cipher);
399  rlb->append(STRING_WITH_LEN("'"));
400  }
401  if (lex->mi.ssl_key)
402  {
403  rlb->append(STRING_WITH_LEN(" MASTER_SSL_KEY = '"));
404  rlb->append(lex->mi.ssl_key);
405  rlb->append(STRING_WITH_LEN("'"));
406  }
407  if (lex->mi.log_file_name)
408  {
409  rlb->append(STRING_WITH_LEN(" MASTER_LOG_FILE = '"));
410  rlb->append(lex->mi.log_file_name);
411  rlb->append(STRING_WITH_LEN("'"));
412  }
413  if (lex->mi.pos)
414  {
415  rlb->append(STRING_WITH_LEN(" MASTER_LOG_POS = "));
416  rlb->append_ulonglong(lex->mi.pos);
417  }
418  if (lex->mi.relay_log_name)
419  {
420  rlb->append(STRING_WITH_LEN(" RELAY_LOG_FILE = '"));
421  rlb->append(lex->mi.relay_log_name);
422  rlb->append(STRING_WITH_LEN("'"));
423  }
424  if (lex->mi.relay_log_pos)
425  {
426  rlb->append(STRING_WITH_LEN(" RELAY_LOG_POS = "));
427  rlb->append_ulonglong(lex->mi.relay_log_pos);
428  }
429 
430  if (lex->mi.ssl_verify_server_cert)
431  {
432  rlb->append(STRING_WITH_LEN(" MASTER_SSL_VERIFY_SERVER_CERT = "));
433  rlb->append(lex->mi.ssl_verify_server_cert == LEX_MASTER_INFO::LEX_MI_ENABLE ? "1" : "0");
434  }
435  if (lex->mi.repl_ignore_server_ids_opt)
436  {
437  bool first= TRUE;
438  rlb->append(STRING_WITH_LEN(" IGNORE_SERVER_IDS = ( "));
439  for (uint i= 0; i < lex->mi.repl_ignore_server_ids.elements; i++)
440  {
441  ulong s_id;
442  get_dynamic(&lex->mi.repl_ignore_server_ids, (uchar*) &s_id, i);
443  if (first)
444  first= FALSE;
445  else
446  rlb->append(STRING_WITH_LEN(", "));
447  rlb->append_ulonglong(s_id);
448  }
449  rlb->append(STRING_WITH_LEN(" )"));
450  }
451  if (lex->mi.heartbeat_opt != LEX_MASTER_INFO::LEX_MI_UNCHANGED)
452  {
453  rlb->append(STRING_WITH_LEN(" MASTER_HEARTBEAT_PERIOD = "));
454  if (lex->mi.heartbeat_opt == LEX_MASTER_INFO::LEX_MI_DISABLE)
455  rlb->append(STRING_WITH_LEN("0"));
456  else
457  {
458  char buf[64];
459  snprintf(buf, 64, "%f", lex->mi.heartbeat_period);
460  rlb->append(buf);
461  }
462  }
463 }
464 
465 
473 static void mysql_rewrite_start_slave(THD *thd, String *rlb)
474 {
475  LEX *lex= thd->lex;
476 
477  if (!lex->slave_connection.password)
478  return;
479 
480  rlb->append(STRING_WITH_LEN("START SLAVE"));
481 
482  if (lex->slave_thd_opt & SLAVE_IO)
483  rlb->append(STRING_WITH_LEN(" IO_THREAD"));
484 
485  /* we have printed the IO THREAD related options */
486  if (lex->slave_thd_opt & SLAVE_IO &&
487  lex->slave_thd_opt & SLAVE_SQL)
488  rlb->append(STRING_WITH_LEN(","));
489 
490  if (lex->slave_thd_opt & SLAVE_SQL)
491  rlb->append(STRING_WITH_LEN(" SQL_THREAD"));
492 
493  /* until options */
494  if (lex->mi.log_file_name || lex->mi.relay_log_name)
495  {
496  rlb->append(STRING_WITH_LEN(" UNTIL"));
497  if (lex->mi.log_file_name)
498  {
499  rlb->append(STRING_WITH_LEN(" MASTER_LOG_FILE = '"));
500  rlb->append(lex->mi.log_file_name);
501  rlb->append(STRING_WITH_LEN("', "));
502  rlb->append(STRING_WITH_LEN("MASTER_LOG_POS = "));
503  rlb->append_ulonglong(lex->mi.pos);
504  }
505 
506  if (lex->mi.relay_log_name)
507  {
508  rlb->append(STRING_WITH_LEN(" RELAY_LOG_FILE = '"));
509  rlb->append(lex->mi.relay_log_name);
510  rlb->append(STRING_WITH_LEN("', "));
511  rlb->append(STRING_WITH_LEN("RELAY_LOG_POS = "));
512  rlb->append_ulonglong(lex->mi.relay_log_pos);
513  }
514  }
515 
516  /* connection options */
517  if (lex->slave_connection.user)
518  {
519  rlb->append(STRING_WITH_LEN(" USER = '"));
520  rlb->append(lex->slave_connection.user);
521  rlb->append(STRING_WITH_LEN("'"));
522  }
523 
524  if (lex->slave_connection.password)
525  rlb->append(STRING_WITH_LEN(" PASSWORD = '<secret>'"));
526 
527  if (lex->slave_connection.plugin_auth)
528  {
529  rlb->append(STRING_WITH_LEN(" DEFAULT_AUTH = '"));
530  rlb->append(lex->slave_connection.plugin_auth);
531  rlb->append(STRING_WITH_LEN("'"));
532  }
533 
534  if (lex->slave_connection.plugin_dir)
535  {
536  rlb->append(STRING_WITH_LEN(" PLUGIN_DIR = '"));
537  rlb->append(lex->slave_connection.plugin_dir);
538  rlb->append(STRING_WITH_LEN("'"));
539  }
540 }
541 
542 
550 static void mysql_rewrite_server_options(THD *thd, String *rlb)
551 {
552  LEX *lex= thd->lex;
553 
554  rlb->append(STRING_WITH_LEN(" OPTIONS ( "));
555 
556  rlb->append(STRING_WITH_LEN("PASSWORD '<secret>'"));
557  append_str(rlb, true, "USER", lex->server_options.username);
558  append_str(rlb, true, "HOST", lex->server_options.host);
559  append_str(rlb, true, "DATABASE", lex->server_options.db);
560  append_str(rlb, true, "OWNER", lex->server_options.owner);
561  append_str(rlb, true, "SOCKET", lex->server_options.socket);
562  append_int(rlb, true, STRING_WITH_LEN("PORT "), lex->server_options.port,
563  lex->server_options.port > 0);
564 
565  rlb->append(STRING_WITH_LEN(" )"));
566 }
567 
568 
576 static void mysql_rewrite_create_server(THD *thd, String *rlb)
577 {
578  LEX *lex= thd->lex;
579 
580  if (!lex->server_options.password)
581  return;
582 
583  rlb->append(STRING_WITH_LEN("CREATE SERVER "));
584 
585  rlb->append(lex->server_options.server_name ?
586  lex->server_options.server_name : "");
587 
588  rlb->append(STRING_WITH_LEN(" FOREIGN DATA WRAPPER '"));
589  rlb->append(lex->server_options.scheme ?
590  lex->server_options.scheme : "");
591  rlb->append(STRING_WITH_LEN("'"));
592 
593  mysql_rewrite_server_options(thd, rlb);
594 }
595 
596 
604 static void mysql_rewrite_alter_server(THD *thd, String *rlb)
605 {
606  LEX *lex= thd->lex;
607 
608  if (!lex->server_options.password)
609  return;
610 
611  rlb->append(STRING_WITH_LEN("ALTER SERVER "));
612 
613  rlb->append(lex->server_options.server_name ?
614  lex->server_options.server_name : "");
615 
616  mysql_rewrite_server_options(thd, rlb);
617 }
618 
619 
620 
621 
629 static void mysql_rewrite_prepare(THD *thd, String *rlb)
630 {
631  LEX *lex= thd->lex;
632 
633  if (lex->prepared_stmt_code_is_varref)
634  return;
635 
636  rlb->append(STRING_WITH_LEN("PREPARE "));
637  rlb->append(lex->prepared_stmt_name.str,
638  lex->prepared_stmt_name.length);
639  rlb->append(STRING_WITH_LEN(" FROM ..."));
640 }
641 
642 
643 
644 
654 void mysql_rewrite_query(THD *thd)
655 {
656  String *rlb= &thd->rewritten_query;
657 
658  rlb->free();
659 
660  if (thd->lex->contains_plaintext_password)
661  {
662  switch(thd->lex->sql_command)
663  {
664  case SQLCOM_GRANT: mysql_rewrite_grant(thd, rlb); break;
665  case SQLCOM_SET_OPTION: mysql_rewrite_set(thd, rlb); break;
666  case SQLCOM_CREATE_USER: mysql_rewrite_create_user(thd, rlb); break;
667  case SQLCOM_CHANGE_MASTER: mysql_rewrite_change_master(thd, rlb); break;
668  case SQLCOM_SLAVE_START: mysql_rewrite_start_slave(thd, rlb); break;
669  case SQLCOM_CREATE_SERVER: mysql_rewrite_create_server(thd, rlb); break;
670  case SQLCOM_ALTER_SERVER: mysql_rewrite_alter_server(thd, rlb); break;
671 
672  /*
673  PREPARE stmt FROM <string> is rewritten so that <string> is
674  not logged. The statement in <string> will in turn be logged
675  by the prepare and the execute functions in sql_prepare.cc.
676  They do call rewrite so they can safely log the statement,
677  but when they call us, it'll be with sql_command set to reflect
678  the statement in question, not SQLCOM_PREPARE or SQLCOM_EXECUTE.
679  Therefore, there is no SQLCOM_EXECUTE case here, and all
680  SQLCOM_PREPARE does is remove <string>; the "other half",
681  i.e. printing what string we prepare from happens when the
682  prepare function calls the logger (and comes by here with
683  sql_command set to the command being prepared).
684  */
685  case SQLCOM_PREPARE: mysql_rewrite_prepare(thd, rlb); break;
686  default: /* unhandled query types are legal. */ break;
687  }
688  }
689 }