1 /*
2 Copyright (c) 2012, 2021, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 /**
26 @file
27
28 @brief
29 MySQL Configuration Utility
30 */
31
32 /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
33 #include "my_config.h"
34 #include <welcome_copyright_notice.h>
35 #include <signal.h>
36 #include <my_dir.h>
37 #include <my_rnd.h>
38 #include "my_aes.h"
39 #include "client_priv.h"
40 #include "my_default.h"
41 #include "my_default_priv.h"
42
43 #define MYSQL_CONFIG_EDITOR_VERSION "1.0"
44 #define MY_LINE_MAX 4096
45 #define MAX_COMMAND_LIMIT 100
46 /*
47 Header length for the login file.
48 4-byte (unused) + LOGIN_KEY_LEN
49 */
50 #define MY_LOGIN_HEADER_LEN (4 + LOGIN_KEY_LEN)
51
52 static int g_fd;
53
54 /*
55 Length of the contents in login file
56 excluding the header part.
57 */
58 static size_t file_size;
59 static const char *opt_user= NULL, *opt_password= NULL, *opt_host=NULL,
60 *opt_login_path= "client", *opt_socket= NULL, *opt_port= NULL;
61
62 static char my_login_file[FN_REFLEN];
63 static char my_key[LOGIN_KEY_LEN];
64
65 static my_bool opt_verbose, opt_all, tty_password= 0, opt_warn,
66 opt_remove_host, opt_remove_pass, opt_remove_user,
67 opt_remove_socket, opt_remove_port, login_path_specified= FALSE;
68
69 static int execute_commands(int command);
70 static int set_command(void);
71 static int remove_command(void);
72 static int print_command(void);
73 static void print_login_path(DYNAMIC_STRING *file_buf, const char *path_name);
74 static void remove_login_path(DYNAMIC_STRING *file_buf, const char *path_name);
75 static char* locate_login_path(DYNAMIC_STRING *file_buf, const char *path_name);
76 static my_bool check_and_create_login_file(void);
77 static void mask_password_and_print(char *buf);
78 static int reset_login_file(bool gen_key);
79
80 static int encrypt_buffer(const char *plain, int plain_len, char cipher[], const int aes_len);
81 static int decrypt_buffer(const char *cipher, int cipher_len, char plain[]);
82 static int encrypt_and_write_file(DYNAMIC_STRING *file_buf);
83 static int read_and_decrypt_file(DYNAMIC_STRING *file_buf);
84 static int do_handle_options(int argc, char *argv[]);
85 static void remove_options(DYNAMIC_STRING *file_buf, const char *path_name);
86 static void remove_option(DYNAMIC_STRING *file_buf, const char *path_name,
87 const char* option_name);
88 void generate_login_key(void);
89 static int read_login_key(void);
90 static int add_header(void);
91 static void my_perror(const char *msg);
92
93 static void verbose_msg(const char *fmt, ...);
94 static void print_version(void);
95 static void usage_program(void);
96 static void usage_command(int command);
97 extern "C" my_bool get_one_option(int optid, const struct my_option *opt,
98 char *argument);
99
100 enum commands {
101 MY_CONFIG_SET,
102 MY_CONFIG_REMOVE,
103 MY_CONFIG_PRINT,
104 MY_CONFIG_RESET,
105 MY_CONFIG_HELP
106 };
107
108 struct my_command_data {
109 const int id;
110 const char *name;
111 const char *description;
112 my_option *options;
113 my_bool (*get_one_option_func)(int optid,
114 const struct my_option *opt,
115 char *argument);
116 };
117
118
119 /* mysql_config_editor utility options. */
120 static struct my_option my_program_long_options[]=
121 {
122 #ifdef NDEBUG
123 {"debug", '#', "This is a non-debug version. Catch this and exit.",
124 0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
125 #else
126 {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
127 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
128 #endif
129 {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
130 NO_ARG, 0, 0, 0, 0, 0, 0},
131 {"verbose", 'v', "Write more information.", &opt_verbose,
132 &opt_verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
133 {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG,
134 NO_ARG, 0, 0, 0, 0, 0, 0},
135 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
136 };
137
138 /* Command-specific options. */
139
140 /* SET command options. */
141 static struct my_option my_set_command_options[]=
142 {
143 {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
144 NO_ARG, 0, 0, 0, 0, 0, 0},
145 {"host", 'h', "Host name to be entered into the login file.", &opt_host,
146 &opt_host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
147 {"login-path", 'G', "Name of the login path to use in the login file. "
148 "(Default : client)", &opt_login_path, &opt_login_path, 0, GET_STR,
149 REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
150 {"password", 'p', "Prompt for password to be entered into the login file.",
151 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
152 {"user", 'u', "User name to be entered into the login file.", &opt_user,
153 &opt_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
154 {"socket", 'S', "Socket path to be entered into login file.", &opt_socket,
155 &opt_socket, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
156 {"port", 'P', "Port number to be entered into login file.", &opt_port,
157 &opt_port, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
158 {"warn", 'w', "Warn and ask for confirmation if set command attempts to "
159 "overwrite an existing login path (enabled by default).",
160 &opt_warn, &opt_warn, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
161 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
162 };
163
164 /* REMOVE command options. */
165 static struct my_option my_remove_command_options[]=
166 {
167 {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
168 NO_ARG, 0, 0, 0, 0, 0, 0},
169 {"host", 'h', "Remove host name from the login path.",
170 &opt_remove_host, &opt_remove_host, 0, GET_BOOL, NO_ARG, 0, 0, 0,
171 0, 0, 0},
172 {"login-path", 'G', "Name of the login path from which options to "
173 "be removed (entire path would be removed if none of user, password, "
174 "host, socket, or port options are specified). (Default : client)",
175 &opt_login_path, &opt_login_path, 0, GET_STR, REQUIRED_ARG,
176 0, 0, 0, 0, 0, 0},
177 {"password", 'p', "Remove password from the login path.",
178 &opt_remove_pass, &opt_remove_pass, 0, GET_BOOL, NO_ARG, 0, 0, 0,
179 0, 0, 0},
180 {"user", 'u', "Remove user name from the login path.", &opt_remove_user,
181 &opt_remove_user, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
182 {"warn", 'w', "Warn and ask for confirmation if remove command attempts "
183 "to remove the default login path (client) if no login path is specified "
184 "(enabled by default).", &opt_warn, &opt_warn, 0, GET_BOOL, NO_ARG, 1,
185 0, 0, 0, 0, 0},
186 {"socket", 'S', "Remove socket path from the login path.", &opt_remove_socket,
187 &opt_remove_socket, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
188 {"port", 'P', "Remove port number from the login path.", &opt_remove_port,
189 &opt_remove_port, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
190 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
191 };
192
193 /* PRINT command options. */
194 static struct my_option my_print_command_options[]=
195 {
196 {"all", OPT_CONFIG_ALL, "Used with print command to print all login paths.",
197 &opt_all, &opt_all, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
198 {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
199 NO_ARG, 0, 0, 0, 0, 0, 0},
200 {"login-path", 'G', "Name of the login path to use in the login file. "
201 "(Default : client)", &opt_login_path, &opt_login_path, 0, GET_STR,
202 REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
203 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
204 };
205
206 /* RESET command options. */
207 static struct my_option my_reset_command_options[]=
208 {
209 {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
210 NO_ARG, 0, 0, 0, 0, 0, 0},
211 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
212 };
213
214 /* HELP command options. */
215 static struct my_option my_help_command_options[]=
216 {
217 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
218 };
219
220 my_bool
my_program_get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)221 my_program_get_one_option(int optid,
222 const struct my_option *opt MY_ATTRIBUTE((unused)),
223 char *argument)
224 {
225 switch(optid) {
226 case '#':
227 DBUG_PUSH(argument ? argument : "d:t:o,/tmp/mysql_config_editor.trace");
228 break;
229 case 'V':
230 print_version();
231 exit(0);
232 break;
233 case '?':
234 usage_program();
235 exit(0);
236 break;
237 }
238 return 0;
239 }
240
241 my_bool
my_set_command_get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)242 my_set_command_get_one_option(int optid,
243 const struct my_option *opt MY_ATTRIBUTE((unused)),
244 char *argument)
245 {
246 switch(optid) {
247 case 'p':
248 tty_password= 1;
249 break;
250 case 'G':
251 if (login_path_specified)
252 {
253 /* Error, we do not support multiple login paths. */
254 my_perror("Error: Use of multiple login paths is not supported. "
255 "Exiting..");
256 return 1;
257 }
258 login_path_specified= TRUE;
259 break;
260 case '?':
261 usage_command(MY_CONFIG_SET);
262 exit(0);
263 break;
264 }
265 return 0;
266 }
267
268 my_bool
my_remove_command_get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)269 my_remove_command_get_one_option(int optid,
270 const struct my_option *opt MY_ATTRIBUTE((unused)),
271 char *argument)
272 {
273 switch(optid) {
274 case 'G':
275 if (login_path_specified)
276 {
277 /* Error, we do not support multiple login paths. */
278 my_perror("Error: Use of multiple login paths is not supported. "
279 "Exiting..");
280 return 1;
281 }
282 login_path_specified= TRUE;
283 break;
284 case '?':
285 usage_command(MY_CONFIG_REMOVE);
286 exit(0);
287 break;
288 }
289 return 0;
290 }
291
292 my_bool
my_print_command_get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)293 my_print_command_get_one_option(int optid,
294 const struct my_option *opt MY_ATTRIBUTE((unused)),
295 char *argument)
296 {
297 switch(optid) {
298 case 'G':
299 if (login_path_specified)
300 {
301 /* Error, we do not support multiple login paths. */
302 my_perror("Error: Use of multiple login paths is not supported. "
303 "Exiting..");
304 return 1;
305 }
306 login_path_specified= TRUE;
307 break;
308 case '?':
309 usage_command(MY_CONFIG_PRINT);
310 exit(0);
311 break;
312 }
313 return 0;
314 }
315
316 my_bool
my_reset_command_get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)317 my_reset_command_get_one_option(int optid,
318 const struct my_option *opt MY_ATTRIBUTE((unused)),
319 char *argument)
320 {
321 switch(optid) {
322 case '?':
323 usage_command(MY_CONFIG_RESET);
324 exit(0);
325 break;
326 }
327 return 0;
328 }
329
330 static struct my_command_data command_data[]=
331 {
332 {
333 MY_CONFIG_SET,
334 "set",
335 "Write a login path to the login file.",
336 my_set_command_options,
337 my_set_command_get_one_option
338 },
339 {
340 MY_CONFIG_REMOVE,
341 "remove",
342 "Remove a login path from the login file.",
343 my_remove_command_options,
344 my_remove_command_get_one_option
345 },
346 {
347 MY_CONFIG_PRINT,
348 "print",
349 "Print the contents of login file in unencrypted form.",
350 my_print_command_options,
351 my_print_command_get_one_option
352 },
353 {
354 MY_CONFIG_RESET,
355 "reset",
356 "Empty the contents of the login file. The file is created\n"
357 "if it does not exist.",
358 my_reset_command_options,
359 my_reset_command_get_one_option
360 },
361 {
362 MY_CONFIG_HELP,
363 "help",
364 "Display a help message and exit.",
365 my_help_command_options,
366 NULL
367 },
368 {
369 0, NULL, NULL, NULL, NULL
370 }
371 };
372
373
main(int argc,char * argv[])374 int main(int argc, char *argv[])
375 {
376 MY_INIT(argv[0]);
377 DBUG_ENTER("main");
378 int command, rc= 0;
379
380 command= do_handle_options(argc, argv);
381
382 if (command > -1)
383 rc= execute_commands(command);
384
385 if (rc != 0)
386 {
387 my_perror("operation failed.");
388 DBUG_RETURN(1);
389 }
390 DBUG_RETURN(0);
391 }
392
393
394 /**
395 Handle all the command line arguments.
396
397 program_name [program options] [command [command options]]
398
399 */
do_handle_options(int argc,char * argv[])400 static int do_handle_options(int argc, char *argv[])
401 {
402 DBUG_ENTER("do_handle_options");
403
404 const char *command_list[MAX_COMMAND_LIMIT + 1];
405 char **saved_argv= argv;
406 char **argv_cmd;
407 char *ptr; /* for free. */
408 int argc_cmd;
409 int rc, i, command= -1;
410
411 if (argc < 2)
412 {
413 usage_program();
414 exit(1);
415 }
416
417 if (!(ptr= (char *) my_malloc(PSI_NOT_INSTRUMENTED,
418 (argc + 2) * sizeof(char *),
419 MYF(MY_WME))))
420 goto error;
421
422 /* Handle program options. */
423
424 /* Prepare a list of supported commands to be used by my_handle_options(). */
425 for (i= 0; (command_data[i].name != NULL) && (i < MAX_COMMAND_LIMIT); i++)
426 command_list[i]= (char *) command_data[i].name;
427 command_list[i]= NULL;
428
429 if ((rc= my_handle_options(&argc, &argv, my_program_long_options,
430 my_program_get_one_option, command_list, FALSE)))
431 exit(rc);
432
433 if (argc == 0) /* No command specified. */
434 goto done;
435
436 for (i= 0; command_data[i].name != NULL; i++)
437 {
438 if (!strcmp(command_data[i].name, argv[0]))
439 {
440 command= i;
441 break;
442 }
443 }
444
445 if (command == -1)
446 goto error;
447
448 /* Handle command options. */
449
450 argv_cmd= (char **)ptr;
451 argc_cmd= argc + 1;
452
453 /* Prepare a command line (argv) using the rest of the options. */
454 argv_cmd[0]= saved_argv[0];
455 memcpy((uchar *) (argv_cmd + 1), (uchar *) (argv),
456 (argc * sizeof(char *)));
457 argv_cmd[argc_cmd]= 0;
458
459 if ((rc= handle_options(&argc_cmd, &argv_cmd,
460 command_data[command].options,
461 command_data[command].get_one_option_func)))
462 exit(rc);
463
464 /* Do not allow multiple commands. */
465 if ( argc_cmd > 1)
466 goto error;
467
468 done:
469 my_free(ptr);
470 DBUG_RETURN(command);
471
472 error:
473 my_free(ptr);
474 usage_program();
475 exit(1);
476 }
477
478
execute_commands(int command)479 static int execute_commands(int command)
480 {
481 DBUG_ENTER("execute_commands");
482 int rc= 0;
483
484 if ((rc= check_and_create_login_file()))
485 goto done;
486
487 switch(command_data[command].id) {
488 case MY_CONFIG_SET :
489 verbose_msg("Executing set command.\n");
490 rc= set_command();
491 break;
492
493 case MY_CONFIG_REMOVE :
494 verbose_msg("Executing remove command.\n");
495 rc= remove_command();
496 break;
497
498 case MY_CONFIG_PRINT :
499 verbose_msg("Executing print command.\n");
500 rc= print_command();
501 break;
502
503 case MY_CONFIG_RESET :
504 verbose_msg("Resetting login file.\n");
505 rc= reset_login_file(1);
506 break;
507
508 case MY_CONFIG_HELP :
509 verbose_msg("Printing usage info.\n");
510 usage_program();
511 break;
512
513 default :
514 my_perror("unknown command.");
515 exit(1);
516 }
517
518 done:
519 my_close(g_fd, MYF(MY_WME));
520
521 DBUG_RETURN(rc);
522 }
523
524
525 /**
526 Execute 'set' command.
527
528 @param void
529
530 @return -1 Error
531 0 Success
532 */
533
set_command(void)534 static int set_command(void)
535 {
536 DBUG_ENTER("set_command");
537
538 DYNAMIC_STRING file_buf, path_buf;
539
540 init_dynamic_string(&path_buf, "", MY_LINE_MAX, MY_LINE_MAX);
541 init_dynamic_string(&file_buf, "", file_size, 3 * MY_LINE_MAX);
542
543 if (tty_password)
544 opt_password= get_tty_password(NullS);
545
546 if (file_size)
547 {
548 if (read_and_decrypt_file(&file_buf) == -1)
549 goto error;
550 }
551
552 dynstr_append(&path_buf, "["); /* --login=path */
553 if (opt_login_path)
554 dynstr_append(&path_buf, opt_login_path);
555 else
556 dynstr_append(&path_buf, "client");
557 dynstr_append(&path_buf, "]");
558
559 if (opt_user) /* --user */
560 {
561 dynstr_append(&path_buf, "\nuser = ");
562 dynstr_append(&path_buf, opt_user);
563 }
564
565 if (opt_password) /* --password */
566 {
567 dynstr_append(&path_buf, "\npassword = ");
568 dynstr_append(&path_buf, opt_password);
569 }
570
571 if (opt_host) /* --host */
572 {
573 dynstr_append(&path_buf, "\nhost = ");
574 dynstr_append(&path_buf, opt_host);
575 }
576
577 if (opt_socket)
578 {
579 dynstr_append(&path_buf, "\nsocket = ");
580 dynstr_append(&path_buf, opt_socket);
581 }
582
583 if (opt_port)
584 {
585 dynstr_append(&path_buf, "\nport = ");
586 dynstr_append(&path_buf, opt_port);
587 }
588
589 dynstr_append(&path_buf, "\n");
590
591 /* Warn if login path already exists */
592 if (opt_warn && ((locate_login_path (&file_buf, opt_login_path))
593 != NULL))
594 {
595 int choice;
596 printf ("WARNING : \'%s\' path already exists and will be "
597 "overwritten. \n Continue? (Press y|Y for Yes, any "
598 "other key for No) : ",
599 opt_login_path);
600 choice= getchar();
601
602 if (choice != (int) 'y' && choice != (int) 'Y')
603 goto done; /* skip */
604 }
605
606 /* Remove the login path. */
607 remove_login_path(&file_buf, opt_login_path);
608
609 /* Append the new login path to the file buffer. */
610 dynstr_append(&file_buf, path_buf.str);
611
612 if (encrypt_and_write_file(&file_buf) == -1)
613 goto error;
614
615 done:
616 dynstr_free(&file_buf);
617 dynstr_free(&path_buf);
618 DBUG_RETURN(0);
619
620 error:
621 dynstr_free(&file_buf);
622 dynstr_free(&path_buf);
623 DBUG_RETURN(-1);
624 }
625
remove_command(void)626 static int remove_command(void) {
627 DBUG_ENTER("remove_command");
628
629 DYNAMIC_STRING file_buf, path_buf;
630
631 init_dynamic_string(&path_buf, "", MY_LINE_MAX, MY_LINE_MAX);
632 init_dynamic_string(&file_buf, "", file_size, 3 * MY_LINE_MAX);
633
634 if (file_size)
635 {
636 if (read_and_decrypt_file(&file_buf) == -1)
637 goto error;
638 }
639 else
640 goto done; /* Nothing to remove, skip.. */
641
642 /* Warn if no login path is specified. */
643 if (opt_warn &&
644 ((locate_login_path (&file_buf, opt_login_path)) != NULL) &&
645 (login_path_specified == FALSE)
646 )
647 {
648 int choice;
649 printf ("WARNING : No login path specified, so options from the default "
650 "login path will be removed.\nContinue? (Press y|Y for Yes, "
651 "any other key for No) : ");
652 choice= getchar();
653
654 if (choice != (int) 'y' && choice != (int) 'Y')
655 goto done; /* skip */
656 }
657
658 remove_options(&file_buf, opt_login_path);
659
660 if (encrypt_and_write_file(&file_buf) == -1)
661 goto error;
662
663 done:
664 dynstr_free(&file_buf);
665 dynstr_free(&path_buf);
666 DBUG_RETURN(0);
667
668 error:
669 dynstr_free(&file_buf);
670 dynstr_free(&path_buf);
671 DBUG_RETURN(-1);
672 }
673
674 /**
675 Execute 'print' command.
676
677 @param void
678
679 @return -1 Error
680 0 Success
681 */
682
print_command(void)683 static int print_command(void)
684 {
685 DBUG_ENTER("print_command");
686 DYNAMIC_STRING file_buf;
687
688 init_dynamic_string(&file_buf, "", file_size, 3 * MY_LINE_MAX);
689
690 if (file_size)
691 {
692 if (read_and_decrypt_file(&file_buf) == -1)
693 goto error;
694 }
695 else
696 goto done; /* Nothing to print, skip..*/
697
698 print_login_path(&file_buf, opt_login_path);
699
700 done:
701 dynstr_free(&file_buf);
702 DBUG_RETURN(0);
703
704 error:
705 dynstr_free(&file_buf);
706 DBUG_RETURN(-1);
707 }
708
709
710 /**
711 Create the login file if it does not exist, check
712 and set its permissions and modes.
713
714 @param void
715
716 @return TRUE Error
717 FALSE Success
718 */
719
check_and_create_login_file(void)720 static my_bool check_and_create_login_file(void)
721 {
722 DBUG_ENTER("check_and_create_login_file");
723
724 MY_STAT stat_info;
725
726 // This is a hack to make it compile. File permissions are different on Windows.
727 #ifdef _WIN32
728 #define S_IRUSR 00400
729 #define S_IWUSR 00200
730 #define S_IRWXU 00700
731 #define S_IRWXG 00070
732 #define S_IRWXO 00007
733 #endif
734
735 const int access_flag= (O_RDWR | O_BINARY);
736 const ushort create_mode= (S_IRUSR | S_IWUSR );
737
738 /* Get the login file name. */
739 if (! my_default_get_login_file(my_login_file, sizeof(my_login_file)))
740 {
741 my_perror("failed to set login file name");
742 goto error;
743 }
744
745 /*
746 NOTE : MYSQL_TEST_LOGIN_FILE env must be a full path,
747 where the directory structure must exist. However the
748 login file will be created if it does not exist.
749 */
750 #ifdef _WIN32
751 if (! (getenv("MYSQL_TEST_LOGIN_FILE")))
752 {
753 /* Check if 'MySQL' directory is in place. */
754 MY_STAT stat_info_dir;
755 char login_dir[FN_REFLEN];
756 size_t size;
757
758 dirname_part(login_dir, my_login_file, &size);
759 /* Remove the trailing '\' */
760 if (is_directory_separator(login_dir[-- size]))
761 login_dir[size]= 0;
762
763 /* Now check if directory exists? */
764 if ( my_stat(login_dir, &stat_info_dir, MYF(0)))
765 {
766 verbose_msg("%s directory exists.\n", login_dir);
767 }
768 else
769 {
770 /* Create the login directory. */
771 verbose_msg("%s directory doesn't exist, creating it.\n", login_dir);
772 if (my_mkdir(login_dir, 0, MYF(0)))
773 {
774 my_perror("failed to create the directory");
775 goto error;
776 }
777 }
778 }
779 #endif
780
781 /* Check for login file's existence and permissions (0600). */
782 if (my_stat(my_login_file, &stat_info, MYF(0)))
783 {
784 verbose_msg("File exists.\n");
785
786 file_size= (size_t) stat_info.st_size;
787
788 #ifdef _WIN32
789 if (1)
790 #else
791 if (!(stat_info.st_mode & (S_IXUSR | S_IRWXG | S_IRWXO)))
792 #endif
793 {
794 verbose_msg("File has the required permission.\nOpening the file.\n");
795 if ((g_fd= my_open(my_login_file, access_flag, MYF(MY_WME))) == -1)
796 {
797 my_perror("couldn't open the file");
798 goto error;
799 }
800 }
801 else
802 {
803 verbose_msg("File does not have the required permission.\n");
804 printf ("WARNING : Login file does not have the required"
805 " file permissions.\nPlease set the mode to 600 &"
806 " run the command again.\n");
807 goto error;
808 }
809 }
810 else
811 {
812 verbose_msg("File does not exist.\nCreating login file.\n");
813 if ((g_fd= my_create(my_login_file, create_mode, access_flag,
814 MYF(MY_WME))) == -1)
815 {
816 my_perror("couldn't create the login file");
817 goto error;
818 }
819 else
820 {
821 verbose_msg("Login file created.\n");
822 my_close(g_fd, MYF(MY_WME));
823 verbose_msg("Opening the file.\n");
824
825 if((g_fd= my_open(my_login_file, access_flag, MYF(MY_WME))) == -1)
826 {
827 my_perror("couldn't open the file");
828 goto error;
829 }
830 file_size= 0;
831 }
832 }
833
834 if (file_size == 0)
835 {
836 generate_login_key();
837 if(add_header() == -1)
838 goto error;
839 }
840 else
841 {
842 if (read_login_key() == -1)
843 goto error;
844 }
845
846 DBUG_RETURN(FALSE);
847
848 error:
849 DBUG_RETURN(TRUE);
850 }
851
852
853 /**
854 Print options under the specified login path. If '--all'
855 option is used, print all the optins stored in the login
856 file.
857
858 @param file_buf [in] Buffer storing the unscrambled login
859 file contents.
860 @param path_name [in] Path name.
861
862 @return void
863
864 */
865
print_login_path(DYNAMIC_STRING * file_buf,const char * path_name)866 static void print_login_path(DYNAMIC_STRING *file_buf, const char *path_name)
867 {
868 DBUG_ENTER("print_login_path");
869
870 char *start= NULL, *end= NULL, temp= '\0';
871
872 if (file_buf->length == 0)
873 goto done; /* Nothing to print. */
874
875 if (opt_all)
876 {
877 start= file_buf->str;
878 end= file_buf->str + file_buf->length;
879 }
880 else
881 {
882 start= locate_login_path(file_buf, path_name);
883 if (! start)
884 /* login path not found, skip..*/
885 goto done;
886
887 end= strstr(start, "\n[");
888 }
889
890 if (end)
891 {
892 temp= *end;
893 *end= '\0';
894 }
895
896 mask_password_and_print(start);
897
898 if (temp != '\0')
899 *end= temp;
900
901 done:
902 DBUG_VOID_RETURN;
903 }
904
905
906 /**
907 Print the specified buffer by masking the actual
908 password string.
909
910 @param buf [in] Buffer to be printed.
911
912 @raturn void
913 */
914
mask_password_and_print(char * buf)915 static void mask_password_and_print(char *buf)
916 {
917 DBUG_ENTER("mask_password_and_print");
918 const char *password_str= "\npassword = ", *mask = "*****";
919 char *next= NULL;
920
921 while ((next= strstr(buf, password_str)) != NULL)
922 {
923 while ( *buf != 0 && buf != next)
924 putc( *(buf ++), stdout);
925 printf("%s", password_str);
926 printf("%s\n", mask);
927 if (*buf == '\n') /* Move past \n' */
928 buf ++;
929
930 /* Ignore the password. */
931 while( *buf && *(buf ++) != '\n')
932 {}
933
934 if ( !opt_all)
935 break;
936 }
937
938 /* Now print the rest of the buffer. */
939 while ( *buf) putc( *(buf ++), stdout);
940 // And a new line.. if required.
941 if (* (buf - 1) != '\n')
942 putc('\n', stdout);
943
944 DBUG_VOID_RETURN;
945 }
946
947
948 /**
949 Remove multiple options from a login path.
950 */
remove_options(DYNAMIC_STRING * file_buf,const char * path_name)951 static void remove_options(DYNAMIC_STRING *file_buf, const char *path_name)
952 {
953 /* If nope of the options are specified remove the entire path. */
954 if (!opt_remove_host && !opt_remove_pass && !opt_remove_user
955 && !opt_remove_socket && !opt_remove_port)
956 {
957 remove_login_path(file_buf, path_name);
958 return;
959 }
960
961 if (opt_remove_user)
962 remove_option(file_buf, path_name, "user");
963
964 if (opt_remove_pass)
965 remove_option(file_buf, path_name, "password");
966
967 if (opt_remove_host)
968 remove_option(file_buf, path_name, "host");
969
970 if (opt_remove_socket)
971 remove_option(file_buf, path_name, "socket");
972
973 if (opt_remove_port)
974 remove_option(file_buf, path_name, "port");
975 }
976
977
978 /**
979 Remove an option from a login path.
980 */
remove_option(DYNAMIC_STRING * file_buf,const char * path_name,const char * option_name)981 static void remove_option(DYNAMIC_STRING *file_buf, const char *path_name,
982 const char* option_name)
983 {
984 DBUG_ENTER("remove_option");
985
986 char *start= NULL, *end= NULL;
987 char *search_str;
988 size_t search_len, shift_len;
989 bool option_found= FALSE;
990
991 search_str= (char *) my_malloc(PSI_NOT_INSTRUMENTED,
992 (uint) strlen(option_name) + 2, MYF(MY_WME));
993 sprintf(search_str, "\n%s", option_name);
994
995 if ((start= locate_login_path(file_buf, path_name)) == NULL)
996 /* login path was not found, skip.. */
997 goto done;
998
999 end= strstr(start, "\n["); /* Next path. */
1000
1001 if (end)
1002 search_len= end - start;
1003 else
1004 search_len= file_buf->length - (start - file_buf->str);
1005
1006 while(search_len > 1)
1007 {
1008 if (!strncmp(start, search_str, strlen(search_str)))
1009 {
1010 /* Option found. */
1011 end= start;
1012 while(*(++ end) != '\n')
1013 {}
1014 option_found= TRUE;
1015 break;
1016 }
1017 else
1018 {
1019 /* Move to next line. */
1020 while( (-- search_len > 1) && (*(++ start) != '\n'))
1021 {}
1022 }
1023 }
1024
1025 if (option_found)
1026 {
1027 shift_len= file_buf->length - (end - file_buf->str);
1028
1029 file_buf->length -= (end - start);
1030
1031 while(shift_len --)
1032 *(start ++)= *(end ++);
1033 *start= '\0';
1034 }
1035
1036 done:
1037 my_free(search_str);
1038 DBUG_VOID_RETURN;
1039 }
1040
1041
1042 /**
1043 Remove the specified login path from the login file.
1044
1045 @param file_buf [in] Buffer storing the unscrambled login
1046 file contents.
1047 @param path_name [in] Path name.
1048
1049 @return void
1050
1051 */
1052
remove_login_path(DYNAMIC_STRING * file_buf,const char * path_name)1053 static void remove_login_path(DYNAMIC_STRING *file_buf, const char *path_name)
1054 {
1055 DBUG_ENTER("remove_login_path");
1056
1057 char *start=NULL, *end= NULL;
1058 int to_move, len, diff;
1059
1060 if((start= locate_login_path(file_buf, path_name)) == NULL)
1061 /* login path was not found, skip.. */
1062 goto done;
1063
1064 end= strstr(start, "\n[");
1065
1066 if (end)
1067 {
1068 end ++; /* Move past '\n' */
1069 len= ((diff= (start - end)) > 0) ? diff : - diff;
1070 to_move= file_buf->length - (end - file_buf->str) ;
1071 }
1072 else
1073 {
1074 *start= '\0';
1075 file_buf->length= ((diff= (file_buf->str - start)) > 0) ? diff : - diff;
1076 goto done;
1077 }
1078
1079 while(to_move --)
1080 *(start ++)= *(end ++);
1081
1082 *start= '\0';
1083 file_buf->length -= len;
1084
1085 done:
1086 DBUG_VOID_RETURN;
1087 }
1088
1089 /**
1090 Remove all the contents from the login file.
1091
1092 @param gen_key [in] Flag to control the generation of
1093 a new key.
1094
1095 @return -1 Error
1096 0 Success
1097 */
1098
reset_login_file(bool gen_key)1099 static int reset_login_file(bool gen_key)
1100 {
1101 DBUG_ENTER("reset_login_file");
1102
1103 if (my_chsize(g_fd, 0, 0, MYF(MY_WME)))
1104 {
1105 verbose_msg("Error while truncating the file.\n");
1106 goto error;
1107 }
1108
1109 /* Seek to the beginning of the file. */
1110 if (my_seek(g_fd, 0L, SEEK_SET, MYF(MY_WME) == MY_FILEPOS_ERROR))
1111 goto error; /* Error. */
1112
1113 if (gen_key)
1114 generate_login_key(); /* Generate a new key. */
1115
1116 if (add_header() == -1)
1117 goto error;
1118
1119 DBUG_RETURN(0);
1120
1121 error:
1122 DBUG_RETURN(0);
1123 }
1124
1125
1126 /**
1127 Find the specified login path in the login file buffer
1128 and return the starting address.
1129
1130 @param file_buf [in] Buffer storing the unscrambled login
1131 file contents.
1132 @param path_name [in] Path name.
1133
1134 @return If found, the starting address of the
1135 login path, NULL otherwise.
1136 */
1137
locate_login_path(DYNAMIC_STRING * file_buf,const char * path_name)1138 static char* locate_login_path(DYNAMIC_STRING *file_buf, const char *path_name)
1139 {
1140 DBUG_ENTER("locate_login_path");
1141
1142 char *addr= NULL;
1143 DYNAMIC_STRING dy_path_name;
1144
1145 init_dynamic_string(&dy_path_name, "", 512, 512);
1146
1147 dynstr_append(&dy_path_name, "\n[");
1148 dynstr_append(&dy_path_name, path_name);
1149 dynstr_append(&dy_path_name, "]");
1150
1151 /* First check if it is the very first login path. */
1152 if (file_buf->str == strstr(file_buf->str, dy_path_name.str + 1))
1153 addr= file_buf->str;
1154 /* If not, scan through the file. */
1155 else
1156 {
1157 addr= strstr(file_buf->str, dy_path_name.str);
1158 if (addr)
1159 addr ++; /* Move past '\n' */
1160 }
1161
1162 dynstr_free(&dy_path_name);
1163 DBUG_RETURN(addr);
1164 }
1165
1166
1167 /**
1168 Encrypt the file buffer and write it to the login file.
1169
1170 @param file_buf [in] Buffer storing the unscrambled login
1171 file contents.
1172
1173 @return -1 Error
1174 0 Success
1175
1176 @note The contents of the file buffer are encrypted
1177 on a line-by-line basis with each line having
1178 the following format :
1179 [<first 4 bytes store cipher-length>|<Next cipher-length
1180 bytes store actual cipher>]
1181 */
1182
encrypt_and_write_file(DYNAMIC_STRING * file_buf)1183 static int encrypt_and_write_file(DYNAMIC_STRING *file_buf)
1184 {
1185 DBUG_ENTER("encrypt_and_write_file");
1186
1187 my_bool done= FALSE;
1188 char cipher[MY_LINE_MAX], *tmp= NULL;
1189 uint bytes_read=0, len= 0;
1190 int enc_len= 0; // Can be negative.
1191
1192 if (reset_login_file(0) == -1)
1193 goto error;
1194
1195 /* Move past key first. */
1196 if (my_seek(g_fd, MY_LOGIN_HEADER_LEN, SEEK_SET, MYF(MY_WME))
1197 != (MY_LOGIN_HEADER_LEN))
1198 goto error; /* Error while seeking. */
1199
1200
1201 tmp= &file_buf->str[bytes_read];
1202
1203 while(! done)
1204 {
1205 len= 0;
1206
1207 while(*tmp++ != '\n')
1208 if (len < (file_buf->length - bytes_read))
1209 len ++;
1210 else
1211 {
1212 done= TRUE;
1213 break;
1214 }
1215
1216 if (done)
1217 break;
1218
1219 if ((enc_len= my_aes_get_size(len + 1, my_aes_128_ecb)) >
1220 (MY_LINE_MAX - (int)MAX_CIPHER_STORE_LEN))
1221 {
1222 my_perror("A parameter to mysql_config_editor exceeds the maximum "
1223 "accepted length. Please review the data you've supplied "
1224 "and try to shorten them permissible length.\n");
1225 goto error;
1226 }
1227
1228 if (encrypt_buffer(&file_buf->str[bytes_read], ++len,
1229 cipher + MAX_CIPHER_STORE_LEN, enc_len) < 0)
1230 goto error;
1231
1232 bytes_read += len;
1233
1234 /* Store cipher length first. */
1235 int4store(cipher, enc_len);
1236
1237 if ((my_write(g_fd, (const uchar *)cipher, enc_len + MAX_CIPHER_STORE_LEN,
1238 MYF(MY_WME))) != (enc_len + MAX_CIPHER_STORE_LEN))
1239 goto error;
1240 }
1241
1242 verbose_msg("Successfully written encrypted data to the login file.\n");
1243
1244 /* Update file_size */
1245 file_size= bytes_read;
1246
1247 DBUG_RETURN(0);
1248
1249 error:
1250 my_perror("couldn't encrypt the file");
1251 DBUG_RETURN(-1);
1252 }
1253
1254
1255 /**
1256 Read the login file, unscramble its contents and store
1257 them into the file buffer.
1258
1259 @param file_buf [in] Buffer for storing the unscrambled login
1260 file contents.
1261
1262 @return -1 Error
1263 0 Success
1264 */
1265
read_and_decrypt_file(DYNAMIC_STRING * file_buf)1266 static int read_and_decrypt_file(DYNAMIC_STRING *file_buf)
1267 {
1268 DBUG_ENTER("read_and_decrypt_file");
1269
1270 char cipher[MY_LINE_MAX], plain[MY_LINE_MAX];
1271 uchar len_buf[MAX_CIPHER_STORE_LEN];
1272 int cipher_len= 0, dec_len= 0;
1273
1274 /* Move past key first. */
1275 if (my_seek(g_fd, MY_LOGIN_HEADER_LEN, SEEK_SET, MYF(MY_WME))
1276 != (MY_LOGIN_HEADER_LEN))
1277 goto error; /* Error while seeking. */
1278
1279 /* First read the length of the cipher. */
1280 while (my_read(g_fd, len_buf, MAX_CIPHER_STORE_LEN,
1281 MYF(MY_WME)) == MAX_CIPHER_STORE_LEN)
1282 {
1283 cipher_len= sint4korr(len_buf);
1284
1285 if (cipher_len > MY_LINE_MAX)
1286 goto error;
1287
1288 /* Now read 'cipher_len' bytes from the file. */
1289 if ((int) my_read(g_fd, (uchar *) cipher, cipher_len, MYF(MY_WME)) == cipher_len)
1290 {
1291 if ((dec_len= decrypt_buffer(cipher, cipher_len, plain)) < 0)
1292 goto error;
1293
1294 plain[dec_len]= 0;
1295 dynstr_append(file_buf, plain);
1296 }
1297 }
1298
1299 verbose_msg("Successfully decrypted the login file.\n");
1300 DBUG_RETURN(0);
1301
1302 error:
1303 my_perror("couldn't decrypt the file");
1304 DBUG_RETURN(-1);
1305 }
1306
1307
1308 /**
1309 Encrypt the given plain text.
1310
1311 @param plain [in] Plain text to be encrypted.
1312 @param plain_len [in] Length of the plain text.
1313 @param cipher [out] Encrypted cipher text.
1314
1315 @return -1 if error encountered,
1316 length encrypted, otherwise.
1317 */
1318
encrypt_buffer(const char * plain,int plain_len,char cipher[],const int aes_len)1319 static int encrypt_buffer(const char *plain, int plain_len, char cipher[], const int aes_len)
1320 {
1321 DBUG_ENTER("encrypt_buffer");
1322
1323 if (my_aes_encrypt((const unsigned char *) plain, plain_len,
1324 (unsigned char *) cipher,
1325 (const unsigned char *) my_key, LOGIN_KEY_LEN,
1326 my_aes_128_ecb, NULL) == aes_len)
1327 DBUG_RETURN(aes_len);
1328
1329 verbose_msg("Error! Couldn't encrypt the buffer.\n");
1330 DBUG_RETURN(-1); /* Error */
1331 }
1332
1333
1334 /**
1335 Decrypt the given cipher text.
1336
1337 @param cipher [in] Cipher text to be decrypted.
1338 @param cipher_len [in] Length of the cipher text.
1339 @param plain [out] Decrypted plain text.
1340
1341 @return -1 if error encountered,
1342 length decrypted, otherwise.
1343 */
1344
decrypt_buffer(const char * cipher,int cipher_len,char plain[])1345 static int decrypt_buffer(const char *cipher, int cipher_len, char plain[])
1346 {
1347 DBUG_ENTER("decrypt_buffer");
1348 int aes_length;
1349
1350 if ((aes_length= my_aes_decrypt((const unsigned char *) cipher, cipher_len,
1351 (unsigned char *) plain,
1352 (const unsigned char *) my_key,
1353 LOGIN_KEY_LEN,
1354 my_aes_128_ecb, NULL)) > 0)
1355 DBUG_RETURN(aes_length);
1356
1357 verbose_msg("Error! Couldn't decrypt the buffer.\n");
1358 DBUG_RETURN(-1); /* Error */
1359 }
1360
1361
1362 /**
1363 Add unused bytes alongwith the to the login key
1364 to the login file.
1365
1366 @return -1 if error encountered,
1367 length written, otherwise.
1368 */
1369
add_header(void)1370 static int add_header(void)
1371 {
1372 DBUG_ENTER("add_header");
1373
1374 /* Reserved for future use. */
1375 const char unused[]= {'\0','\0','\0','\0'};
1376
1377 /* Write 'unused' bytes first. */
1378 if ((my_write(g_fd, (const uchar *) unused, 4, MYF(MY_WME))) != 4)
1379 goto error;
1380
1381 /* Write the login key. */
1382 if ((my_write(g_fd, (const uchar *)my_key, LOGIN_KEY_LEN, MYF(MY_WME)))
1383 != LOGIN_KEY_LEN)
1384 goto error;
1385
1386 verbose_msg("Key successfully written to the file.\n");
1387 DBUG_RETURN(MY_LOGIN_HEADER_LEN);
1388
1389 error:
1390 my_perror("file write operation failed");
1391 DBUG_RETURN(-1);
1392 }
1393
1394
1395 /**
1396 Algorithm to generate key.
1397 */
1398
generate_login_key()1399 void generate_login_key()
1400 {
1401 DBUG_ENTER("generate_login_key");
1402 struct rand_struct rnd;
1403
1404 verbose_msg("Generating a new key.\n");
1405 /* Get a sequence of random non-printable ASCII */
1406 for (uint i= 0; i < LOGIN_KEY_LEN; i++)
1407 my_key[i]= (char)((int)(my_rnd_ssl(&rnd) * 100000) % 32);
1408
1409 DBUG_VOID_RETURN;
1410 }
1411
1412 /**
1413 Read the stored login key.
1414
1415 @return -1 Error
1416 0 Success
1417 */
1418
read_login_key(void)1419 static int read_login_key(void)
1420 {
1421 DBUG_ENTER("read_login_key");
1422
1423 verbose_msg("Reading the login key.\n");
1424 /* Move past the unused buffer. */
1425 if (my_seek(g_fd, 4, SEEK_SET, MYF(MY_WME)) != 4)
1426 goto error; /* Error while seeking. */
1427
1428 if (my_read(g_fd, (uchar *)my_key, LOGIN_KEY_LEN, MYF(MY_WME))
1429 != LOGIN_KEY_LEN)
1430 goto error; /* Error while reading. */
1431
1432 verbose_msg("Login key read successfully.\n");
1433 DBUG_RETURN(0);
1434
1435 error:
1436 my_perror("file read operation failed");
1437 DBUG_RETURN(-1);
1438 }
1439
1440
verbose_msg(const char * fmt,...)1441 static void verbose_msg(const char *fmt, ...)
1442 {
1443 DBUG_ENTER("verbose_msg");
1444 va_list args;
1445
1446 if (!opt_verbose)
1447 DBUG_VOID_RETURN;
1448
1449 va_start(args, fmt);
1450 vfprintf(stderr, fmt, args);
1451 va_end(args);
1452 fflush(stderr);
1453
1454 DBUG_VOID_RETURN;
1455 }
1456
my_perror(const char * msg)1457 static void my_perror(const char *msg)
1458 {
1459 char errbuf[MYSYS_STRERROR_SIZE];
1460
1461 if (errno == 0)
1462 fprintf(stderr, "%s\n", (msg) ? msg : "");
1463 else
1464 fprintf(stderr, "%s : %s\n", (msg) ? msg : "",
1465 my_strerror(errbuf, sizeof(errbuf), errno));
1466 // reset errno
1467 errno= 0;
1468 }
1469
usage_command(int command)1470 static void usage_command(int command)
1471 {
1472 print_version();
1473 puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2012"));
1474 puts("MySQL Configuration Utility.");
1475 printf("\nDescription: %s\n", command_data[command].description);
1476 printf("Usage: %s [program options] [%s [command options]]\n",
1477 my_progname, command_data[command].name);
1478 my_print_help(command_data[command].options);
1479 my_print_variables(command_data[command].options);
1480 }
1481
1482
usage_program(void)1483 static void usage_program(void)
1484 {
1485 print_version();
1486 puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2012"));
1487 puts("MySQL Configuration Utility.");
1488 printf("Usage: %s [program options] [command [command options]]\n",
1489 my_progname);
1490 my_print_help(my_program_long_options);
1491 my_print_variables(my_program_long_options);
1492 puts("\nWhere command can be any one of the following :\n\
1493 set [command options] Sets user name/password/host name/socket/port\n\
1494 for a given login path (section).\n\
1495 remove [command options] Remove a login path from the login file.\n\
1496 print [command options] Print all the options for a specified\n\
1497 login path.\n\
1498 reset [command options] Deletes the contents of the login file.\n\
1499 help Display this usage/help information.\n");
1500 }
1501
1502
print_version(void)1503 static void print_version(void) {
1504 printf ("%s Ver %s Distrib %s, for %s on %s\n", my_progname,
1505 MYSQL_CONFIG_EDITOR_VERSION, MYSQL_SERVER_VERSION,
1506 SYSTEM_TYPE, MACHINE_TYPE);
1507 }
1508
1509