mysql5/mysql-5.7.27/client/mysql_config_editor.cc

1502 lines
38 KiB
C++

/*
Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
@file
@brief
MySQL Configuration Utility
*/
/* ORACLE_WELCOME_COPYRIGHT_NOTICE */
#include "my_config.h"
#include <welcome_copyright_notice.h>
#include <signal.h>
#include <my_dir.h>
#include <my_rnd.h>
#include "my_aes.h"
#include "client_priv.h"
#include "my_default.h"
#include "my_default_priv.h"
#define MYSQL_CONFIG_EDITOR_VERSION "1.0"
#define MY_LINE_MAX 4096
#define MAX_COMMAND_LIMIT 100
/*
Header length for the login file.
4-byte (unused) + LOGIN_KEY_LEN
*/
#define MY_LOGIN_HEADER_LEN (4 + LOGIN_KEY_LEN)
static int g_fd;
/*
Length of the contents in login file
excluding the header part.
*/
static size_t file_size;
static const char *opt_user= NULL, *opt_password= NULL, *opt_host=NULL,
*opt_login_path= "client", *opt_socket= NULL, *opt_port= NULL;
static char my_login_file[FN_REFLEN];
static char my_key[LOGIN_KEY_LEN];
static my_bool opt_verbose, opt_all, tty_password= 0, opt_warn,
opt_remove_host, opt_remove_pass, opt_remove_user,
opt_remove_socket, opt_remove_port, login_path_specified= FALSE;
static int execute_commands(int command);
static int set_command(void);
static int remove_command(void);
static int print_command(void);
static void print_login_path(DYNAMIC_STRING *file_buf, const char *path_name);
static void remove_login_path(DYNAMIC_STRING *file_buf, const char *path_name);
static char* locate_login_path(DYNAMIC_STRING *file_buf, const char *path_name);
static my_bool check_and_create_login_file(void);
static void mask_password_and_print(char *buf);
static int reset_login_file(bool gen_key);
static int encrypt_buffer(const char *plain, int plain_len, char cipher[], const int aes_len);
static int decrypt_buffer(const char *cipher, int cipher_len, char plain[]);
static int encrypt_and_write_file(DYNAMIC_STRING *file_buf);
static int read_and_decrypt_file(DYNAMIC_STRING *file_buf);
static int do_handle_options(int argc, char *argv[]);
static void remove_options(DYNAMIC_STRING *file_buf, const char *path_name);
static void remove_option(DYNAMIC_STRING *file_buf, const char *path_name,
const char* option_name);
void generate_login_key(void);
static int read_login_key(void);
static int add_header(void);
static void my_perror(const char *msg);
static void verbose_msg(const char *fmt, ...);
static void print_version(void);
static void usage_program(void);
static void usage_command(int command);
extern "C" my_bool get_one_option(int optid, const struct my_option *opt,
char *argument);
enum commands {
MY_CONFIG_SET,
MY_CONFIG_REMOVE,
MY_CONFIG_PRINT,
MY_CONFIG_RESET,
MY_CONFIG_HELP
};
struct my_command_data {
const int id;
const char *name;
const char *description;
my_option *options;
my_bool (*get_one_option_func)(int optid,
const struct my_option *opt,
char *argument);
};
/* mysql_config_editor utility options. */
static struct my_option my_program_long_options[]=
{
#ifdef DBUG_OFF
{"debug", '#', "This is a non-debug version. Catch this and exit.",
0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
#else
{"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
#endif
{"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
NO_ARG, 0, 0, 0, 0, 0, 0},
{"verbose", 'v', "Write more information.", &opt_verbose,
&opt_verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG,
NO_ARG, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
/* Command-specific options. */
/* SET command options. */
static struct my_option my_set_command_options[]=
{
{"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
NO_ARG, 0, 0, 0, 0, 0, 0},
{"host", 'h', "Host name to be entered into the login file.", &opt_host,
&opt_host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"login-path", 'G', "Name of the login path to use in the login file. "
"(Default : client)", &opt_login_path, &opt_login_path, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"password", 'p', "Prompt for password to be entered into the login file.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"user", 'u', "User name to be entered into the login file.", &opt_user,
&opt_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"socket", 'S', "Socket path to be entered into login file.", &opt_socket,
&opt_socket, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"port", 'P', "Port number to be entered into login file.", &opt_port,
&opt_port, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"warn", 'w', "Warn and ask for confirmation if set command attempts to "
"overwrite an existing login path (enabled by default).",
&opt_warn, &opt_warn, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
/* REMOVE command options. */
static struct my_option my_remove_command_options[]=
{
{"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
NO_ARG, 0, 0, 0, 0, 0, 0},
{"host", 'h', "Remove host name from the login path.",
&opt_remove_host, &opt_remove_host, 0, GET_BOOL, NO_ARG, 0, 0, 0,
0, 0, 0},
{"login-path", 'G', "Name of the login path from which options to "
"be removed (entire path would be removed if none of user, password, "
"host, socket, or port options are specified). (Default : client)",
&opt_login_path, &opt_login_path, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
{"password", 'p', "Remove password from the login path.",
&opt_remove_pass, &opt_remove_pass, 0, GET_BOOL, NO_ARG, 0, 0, 0,
0, 0, 0},
{"user", 'u', "Remove user name from the login path.", &opt_remove_user,
&opt_remove_user, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"warn", 'w', "Warn and ask for confirmation if remove command attempts "
"to remove the default login path (client) if no login path is specified "
"(enabled by default).", &opt_warn, &opt_warn, 0, GET_BOOL, NO_ARG, 1,
0, 0, 0, 0, 0},
{"socket", 'S', "Remove socket path from the login path.", &opt_remove_socket,
&opt_remove_socket, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"port", 'P', "Remove port number from the login path.", &opt_remove_port,
&opt_remove_port, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
/* PRINT command options. */
static struct my_option my_print_command_options[]=
{
{"all", OPT_CONFIG_ALL, "Used with print command to print all login paths.",
&opt_all, &opt_all, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
NO_ARG, 0, 0, 0, 0, 0, 0},
{"login-path", 'G', "Name of the login path to use in the login file. "
"(Default : client)", &opt_login_path, &opt_login_path, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
/* RESET command options. */
static struct my_option my_reset_command_options[]=
{
{"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
NO_ARG, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
/* HELP command options. */
static struct my_option my_help_command_options[]=
{
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
my_bool
my_program_get_one_option(int optid,
const struct my_option *opt MY_ATTRIBUTE((unused)),
char *argument)
{
switch(optid) {
case '#':
DBUG_PUSH(argument ? argument : "d:t:o,/tmp/mysql_config_editor.trace");
break;
case 'V':
print_version();
exit(0);
break;
case '?':
usage_program();
exit(0);
break;
}
return 0;
}
my_bool
my_set_command_get_one_option(int optid,
const struct my_option *opt MY_ATTRIBUTE((unused)),
char *argument)
{
switch(optid) {
case 'p':
tty_password= 1;
break;
case 'G':
if (login_path_specified)
{
/* Error, we do not support multiple login paths. */
my_perror("Error: Use of multiple login paths is not supported. "
"Exiting..");
return 1;
}
login_path_specified= TRUE;
break;
case '?':
usage_command(MY_CONFIG_SET);
exit(0);
break;
}
return 0;
}
my_bool
my_remove_command_get_one_option(int optid,
const struct my_option *opt MY_ATTRIBUTE((unused)),
char *argument)
{
switch(optid) {
case 'G':
if (login_path_specified)
{
/* Error, we do not support multiple login paths. */
my_perror("Error: Use of multiple login paths is not supported. "
"Exiting..");
return 1;
}
login_path_specified= TRUE;
break;
case '?':
usage_command(MY_CONFIG_REMOVE);
exit(0);
break;
}
return 0;
}
my_bool
my_print_command_get_one_option(int optid,
const struct my_option *opt MY_ATTRIBUTE((unused)),
char *argument)
{
switch(optid) {
case 'G':
if (login_path_specified)
{
/* Error, we do not support multiple login paths. */
my_perror("Error: Use of multiple login paths is not supported. "
"Exiting..");
return 1;
}
login_path_specified= TRUE;
break;
case '?':
usage_command(MY_CONFIG_PRINT);
exit(0);
break;
}
return 0;
}
my_bool
my_reset_command_get_one_option(int optid,
const struct my_option *opt MY_ATTRIBUTE((unused)),
char *argument)
{
switch(optid) {
case '?':
usage_command(MY_CONFIG_RESET);
exit(0);
break;
}
return 0;
}
static struct my_command_data command_data[]=
{
{
MY_CONFIG_SET,
"set",
"Write a login path to the login file.",
my_set_command_options,
my_set_command_get_one_option
},
{
MY_CONFIG_REMOVE,
"remove",
"Remove a login path from the login file.",
my_remove_command_options,
my_remove_command_get_one_option
},
{
MY_CONFIG_PRINT,
"print",
"Print the contents of login file in unencrypted form.",
my_print_command_options,
my_print_command_get_one_option
},
{
MY_CONFIG_RESET,
"reset",
"Empty the contents of the login file. The file is created\n"
"if it does not exist.",
my_reset_command_options,
my_reset_command_get_one_option
},
{
MY_CONFIG_HELP,
"help",
"Display a help message and exit.",
my_help_command_options,
NULL
},
{
0, NULL, NULL, NULL, NULL
}
};
int main(int argc, char *argv[])
{
MY_INIT(argv[0]);
DBUG_ENTER("main");
int command, rc= 0;
command= do_handle_options(argc, argv);
if (command > -1)
rc= execute_commands(command);
if (rc != 0)
{
my_perror("operation failed.");
DBUG_RETURN(1);
}
DBUG_RETURN(0);
}
/**
Handle all the command line arguments.
program_name [program options] [command [command options]]
*/
static int do_handle_options(int argc, char *argv[])
{
DBUG_ENTER("do_handle_options");
const char *command_list[MAX_COMMAND_LIMIT + 1];
char **saved_argv= argv;
char **argv_cmd;
char *ptr; /* for free. */
int argc_cmd;
int rc, i, command= -1;
if (argc < 2)
{
usage_program();
exit(1);
}
if (!(ptr= (char *) my_malloc(PSI_NOT_INSTRUMENTED,
(argc + 2) * sizeof(char *),
MYF(MY_WME))))
goto error;
/* Handle program options. */
/* Prepare a list of supported commands to be used by my_handle_options(). */
for (i= 0; (command_data[i].name != NULL) && (i < MAX_COMMAND_LIMIT); i++)
command_list[i]= (char *) command_data[i].name;
command_list[i]= NULL;
if ((rc= my_handle_options(&argc, &argv, my_program_long_options,
my_program_get_one_option, command_list, FALSE)))
exit(rc);
if (argc == 0) /* No command specified. */
goto done;
for (i= 0; command_data[i].name != NULL; i++)
{
if (!strcmp(command_data[i].name, argv[0]))
{
command= i;
break;
}
}
if (command == -1)
goto error;
/* Handle command options. */
argv_cmd= (char **)ptr;
argc_cmd= argc + 1;
/* Prepare a command line (argv) using the rest of the options. */
argv_cmd[0]= saved_argv[0];
memcpy((uchar *) (argv_cmd + 1), (uchar *) (argv),
(argc * sizeof(char *)));
argv_cmd[argc_cmd]= 0;
if ((rc= handle_options(&argc_cmd, &argv_cmd,
command_data[command].options,
command_data[command].get_one_option_func)))
exit(rc);
/* Do not allow multiple commands. */
if ( argc_cmd > 1)
goto error;
done:
my_free(ptr);
DBUG_RETURN(command);
error:
my_free(ptr);
usage_program();
exit(1);
}
static int execute_commands(int command)
{
DBUG_ENTER("execute_commands");
int rc= 0;
if ((rc= check_and_create_login_file()))
goto done;
switch(command_data[command].id) {
case MY_CONFIG_SET :
verbose_msg("Executing set command.\n");
rc= set_command();
break;
case MY_CONFIG_REMOVE :
verbose_msg("Executing remove command.\n");
rc= remove_command();
break;
case MY_CONFIG_PRINT :
verbose_msg("Executing print command.\n");
rc= print_command();
break;
case MY_CONFIG_RESET :
verbose_msg("Resetting login file.\n");
rc= reset_login_file(1);
break;
case MY_CONFIG_HELP :
verbose_msg("Printing usage info.\n");
usage_program();
break;
default :
my_perror("unknown command.");
exit(1);
}
done:
my_close(g_fd, MYF(MY_WME));
DBUG_RETURN(rc);
}
/**
Execute 'set' command.
@param void
@return -1 Error
0 Success
*/
static int set_command(void)
{
DBUG_ENTER("set_command");
DYNAMIC_STRING file_buf, path_buf;
init_dynamic_string(&path_buf, "", MY_LINE_MAX, MY_LINE_MAX);
init_dynamic_string(&file_buf, "", file_size, 3 * MY_LINE_MAX);
if (tty_password)
opt_password= get_tty_password(NullS);
if (file_size)
{
if (read_and_decrypt_file(&file_buf) == -1)
goto error;
}
dynstr_append(&path_buf, "["); /* --login=path */
if (opt_login_path)
dynstr_append(&path_buf, opt_login_path);
else
dynstr_append(&path_buf, "client");
dynstr_append(&path_buf, "]");
if (opt_user) /* --user */
{
dynstr_append(&path_buf, "\nuser = ");
dynstr_append(&path_buf, opt_user);
}
if (opt_password) /* --password */
{
dynstr_append(&path_buf, "\npassword = ");
dynstr_append(&path_buf, opt_password);
}
if (opt_host) /* --host */
{
dynstr_append(&path_buf, "\nhost = ");
dynstr_append(&path_buf, opt_host);
}
if (opt_socket)
{
dynstr_append(&path_buf, "\nsocket = ");
dynstr_append(&path_buf, opt_socket);
}
if (opt_port)
{
dynstr_append(&path_buf, "\nport = ");
dynstr_append(&path_buf, opt_port);
}
dynstr_append(&path_buf, "\n");
/* Warn if login path already exists */
if (opt_warn && ((locate_login_path (&file_buf, opt_login_path))
!= NULL))
{
int choice;
printf ("WARNING : \'%s\' path already exists and will be "
"overwritten. \n Continue? (Press y|Y for Yes, any "
"other key for No) : ",
opt_login_path);
choice= getchar();
if (choice != (int) 'y' && choice != (int) 'Y')
goto done; /* skip */
}
/* Remove the login path. */
remove_login_path(&file_buf, opt_login_path);
/* Append the new login path to the file buffer. */
dynstr_append(&file_buf, path_buf.str);
if (encrypt_and_write_file(&file_buf) == -1)
goto error;
done:
dynstr_free(&file_buf);
dynstr_free(&path_buf);
DBUG_RETURN(0);
error:
dynstr_free(&file_buf);
dynstr_free(&path_buf);
DBUG_RETURN(-1);
}
static int remove_command(void) {
DBUG_ENTER("remove_command");
DYNAMIC_STRING file_buf, path_buf;
init_dynamic_string(&path_buf, "", MY_LINE_MAX, MY_LINE_MAX);
init_dynamic_string(&file_buf, "", file_size, 3 * MY_LINE_MAX);
if (file_size)
{
if (read_and_decrypt_file(&file_buf) == -1)
goto error;
}
else
goto done; /* Nothing to remove, skip.. */
/* Warn if no login path is specified. */
if (opt_warn &&
((locate_login_path (&file_buf, opt_login_path)) != NULL) &&
(login_path_specified == FALSE)
)
{
int choice;
printf ("WARNING : No login path specified, so options from the default "
"login path will be removed.\nContinue? (Press y|Y for Yes, "
"any other key for No) : ");
choice= getchar();
if (choice != (int) 'y' && choice != (int) 'Y')
goto done; /* skip */
}
remove_options(&file_buf, opt_login_path);
if (encrypt_and_write_file(&file_buf) == -1)
goto error;
done:
dynstr_free(&file_buf);
dynstr_free(&path_buf);
DBUG_RETURN(0);
error:
dynstr_free(&file_buf);
dynstr_free(&path_buf);
DBUG_RETURN(-1);
}
/**
Execute 'print' command.
@param void
@return -1 Error
0 Success
*/
static int print_command(void)
{
DBUG_ENTER("print_command");
DYNAMIC_STRING file_buf;
init_dynamic_string(&file_buf, "", file_size, 3 * MY_LINE_MAX);
if (file_size)
{
if (read_and_decrypt_file(&file_buf) == -1)
goto error;
}
else
goto done; /* Nothing to print, skip..*/
print_login_path(&file_buf, opt_login_path);
done:
dynstr_free(&file_buf);
DBUG_RETURN(0);
error:
dynstr_free(&file_buf);
DBUG_RETURN(-1);
}
/**
Create the login file if it does not exist, check
and set its permissions and modes.
@param void
@return TRUE Error
FALSE Success
*/
static my_bool check_and_create_login_file(void)
{
DBUG_ENTER("check_and_create_login_file");
MY_STAT stat_info;
// This is a hack to make it compile. File permissions are different on Windows.
#ifdef _WIN32
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IRWXU 00700
#define S_IRWXG 00070
#define S_IRWXO 00007
#endif
const int access_flag= (O_RDWR | O_BINARY);
const ushort create_mode= (S_IRUSR | S_IWUSR );
/* Get the login file name. */
if (! my_default_get_login_file(my_login_file, sizeof(my_login_file)))
{
my_perror("failed to set login file name");
goto error;
}
/*
NOTE : MYSQL_TEST_LOGIN_FILE env must be a full path,
where the directory structure must exist. However the
login file will be created if it does not exist.
*/
#ifdef _WIN32
if (! (getenv("MYSQL_TEST_LOGIN_FILE")))
{
/* Check if 'MySQL' directory is in place. */
MY_STAT stat_info_dir;
char login_dir[FN_REFLEN];
size_t size;
dirname_part(login_dir, my_login_file, &size);
/* Remove the trailing '\' */
if (is_directory_separator(login_dir[-- size]))
login_dir[size]= 0;
/* Now check if directory exists? */
if ( my_stat(login_dir, &stat_info_dir, MYF(0)))
{
verbose_msg("%s directory exists.\n", login_dir);
}
else
{
/* Create the login directory. */
verbose_msg("%s directory doesn't exist, creating it.\n", login_dir);
if (my_mkdir(login_dir, 0, MYF(0)))
{
my_perror("failed to create the directory");
goto error;
}
}
}
#endif
/* Check for login file's existence and permissions (0600). */
if (my_stat(my_login_file, &stat_info, MYF(0)))
{
verbose_msg("File exists.\n");
file_size= (size_t) stat_info.st_size;
#ifdef _WIN32
if (1)
#else
if (!(stat_info.st_mode & (S_IXUSR | S_IRWXG | S_IRWXO)))
#endif
{
verbose_msg("File has the required permission.\nOpening the file.\n");
if ((g_fd= my_open(my_login_file, access_flag, MYF(MY_WME))) == -1)
{
my_perror("couldn't open the file");
goto error;
}
}
else
{
verbose_msg("File does not have the required permission.\n");
printf ("WARNING : Login file does not have the required"
" file permissions.\nPlease set the mode to 600 &"
" run the command again.\n");
goto error;
}
}
else
{
verbose_msg("File does not exist.\nCreating login file.\n");
if ((g_fd= my_create(my_login_file, create_mode, access_flag,
MYF(MY_WME))) == -1)
{
my_perror("couldn't create the login file");
goto error;
}
else
{
verbose_msg("Login file created.\n");
my_close(g_fd, MYF(MY_WME));
verbose_msg("Opening the file.\n");
if((g_fd= my_open(my_login_file, access_flag, MYF(MY_WME))) == -1)
{
my_perror("couldn't open the file");
goto error;
}
file_size= 0;
}
}
if (file_size == 0)
{
generate_login_key();
if(add_header() == -1)
goto error;
}
else
{
if (read_login_key() == -1)
goto error;
}
DBUG_RETURN(FALSE);
error:
DBUG_RETURN(TRUE);
}
/**
Print options under the specified login path. If '--all'
option is used, print all the optins stored in the login
file.
@param file_buf [in] Buffer storing the unscrambled login
file contents.
@param path_name [in] Path name.
@return void
*/
static void print_login_path(DYNAMIC_STRING *file_buf, const char *path_name)
{
DBUG_ENTER("print_login_path");
char *start= NULL, *end= NULL, temp= '\0';
if (file_buf->length == 0)
goto done; /* Nothing to print. */
if (opt_all)
{
start= file_buf->str;
end= file_buf->str + file_buf->length;
}
else
{
start= locate_login_path(file_buf, path_name);
if (! start)
/* login path not found, skip..*/
goto done;
end= strstr(start, "\n[");
}
if (end)
{
temp= *end;
*end= '\0';
}
mask_password_and_print(start);
if (temp != '\0')
*end= temp;
done:
DBUG_VOID_RETURN;
}
/**
Print the specified buffer by masking the actual
password string.
@param buf [in] Buffer to be printed.
@raturn void
*/
static void mask_password_and_print(char *buf)
{
DBUG_ENTER("mask_password_and_print");
const char *password_str= "\npassword = ", *mask = "*****";
char *next= NULL;
while ((next= strstr(buf, password_str)) != NULL)
{
while ( *buf != 0 && buf != next)
putc( *(buf ++), stdout);
printf("%s", password_str);
printf("%s\n", mask);
if (*buf == '\n') /* Move past \n' */
buf ++;
/* Ignore the password. */
while( *buf && *(buf ++) != '\n')
{}
if ( !opt_all)
break;
}
/* Now print the rest of the buffer. */
while ( *buf) putc( *(buf ++), stdout);
// And a new line.. if required.
if (* (buf - 1) != '\n')
putc('\n', stdout);
DBUG_VOID_RETURN;
}
/**
Remove multiple options from a login path.
*/
static void remove_options(DYNAMIC_STRING *file_buf, const char *path_name)
{
/* If nope of the options are specified remove the entire path. */
if (!opt_remove_host && !opt_remove_pass && !opt_remove_user
&& !opt_remove_socket && !opt_remove_port)
{
remove_login_path(file_buf, path_name);
return;
}
if (opt_remove_user)
remove_option(file_buf, path_name, "user");
if (opt_remove_pass)
remove_option(file_buf, path_name, "password");
if (opt_remove_host)
remove_option(file_buf, path_name, "host");
if (opt_remove_socket)
remove_option(file_buf, path_name, "socket");
if (opt_remove_port)
remove_option(file_buf, path_name, "port");
}
/**
Remove an option from a login path.
*/
static void remove_option(DYNAMIC_STRING *file_buf, const char *path_name,
const char* option_name)
{
DBUG_ENTER("remove_option");
char *start= NULL, *end= NULL;
char *search_str;
size_t search_len, shift_len;
bool option_found= FALSE;
search_str= (char *) my_malloc(PSI_NOT_INSTRUMENTED,
(uint) strlen(option_name) + 2, MYF(MY_WME));
sprintf(search_str, "\n%s", option_name);
if ((start= locate_login_path(file_buf, path_name)) == NULL)
/* login path was not found, skip.. */
goto done;
end= strstr(start, "\n["); /* Next path. */
if (end)
search_len= end - start;
else
search_len= file_buf->length - (start - file_buf->str);
while(search_len > 1)
{
if (!strncmp(start, search_str, strlen(search_str)))
{
/* Option found. */
end= start;
while(*(++ end) != '\n')
{}
option_found= TRUE;
break;
}
else
{
/* Move to next line. */
while( (-- search_len > 1) && (*(++ start) != '\n'))
{}
}
}
if (option_found)
{
shift_len= file_buf->length - (end - file_buf->str);
file_buf->length -= (end - start);
while(shift_len --)
*(start ++)= *(end ++);
*start= '\0';
}
done:
my_free(search_str);
DBUG_VOID_RETURN;
}
/**
Remove the specified login path from the login file.
@param file_buf [in] Buffer storing the unscrambled login
file contents.
@param path_name [in] Path name.
@return void
*/
static void remove_login_path(DYNAMIC_STRING *file_buf, const char *path_name)
{
DBUG_ENTER("remove_login_path");
char *start=NULL, *end= NULL;
int to_move, len, diff;
if((start= locate_login_path(file_buf, path_name)) == NULL)
/* login path was not found, skip.. */
goto done;
end= strstr(start, "\n[");
if (end)
{
end ++; /* Move past '\n' */
len= ((diff= (start - end)) > 0) ? diff : - diff;
to_move= file_buf->length - (end - file_buf->str) ;
}
else
{
*start= '\0';
file_buf->length= ((diff= (file_buf->str - start)) > 0) ? diff : - diff;
goto done;
}
while(to_move --)
*(start ++)= *(end ++);
*start= '\0';
file_buf->length -= len;
done:
DBUG_VOID_RETURN;
}
/**
Remove all the contents from the login file.
@param gen_key [in] Flag to control the generation of
a new key.
@return -1 Error
0 Success
*/
static int reset_login_file(bool gen_key)
{
DBUG_ENTER("reset_login_file");
if (my_chsize(g_fd, 0, 0, MYF(MY_WME)))
{
verbose_msg("Error while truncating the file.\n");
goto error;
}
/* Seek to the beginning of the file. */
if (my_seek(g_fd, 0L, SEEK_SET, MYF(MY_WME) == MY_FILEPOS_ERROR))
goto error; /* Error. */
if (gen_key)
generate_login_key(); /* Generate a new key. */
if (add_header() == -1)
goto error;
DBUG_RETURN(0);
error:
DBUG_RETURN(0);
}
/**
Find the specified login path in the login file buffer
and return the starting address.
@param file_buf [in] Buffer storing the unscrambled login
file contents.
@param path_name [in] Path name.
@return If found, the starting address of the
login path, NULL otherwise.
*/
static char* locate_login_path(DYNAMIC_STRING *file_buf, const char *path_name)
{
DBUG_ENTER("locate_login_path");
char *addr= NULL;
DYNAMIC_STRING dy_path_name;
init_dynamic_string(&dy_path_name, "", 512, 512);
dynstr_append(&dy_path_name, "\n[");
dynstr_append(&dy_path_name, path_name);
dynstr_append(&dy_path_name, "]");
/* First check if it is the very first login path. */
if (file_buf->str == strstr(file_buf->str, dy_path_name.str + 1))
addr= file_buf->str;
/* If not, scan through the file. */
else
{
addr= strstr(file_buf->str, dy_path_name.str);
if (addr)
addr ++; /* Move past '\n' */
}
dynstr_free(&dy_path_name);
DBUG_RETURN(addr);
}
/**
Encrypt the file buffer and write it to the login file.
@param file_buf [in] Buffer storing the unscrambled login
file contents.
@return -1 Error
0 Success
@note The contents of the file buffer are encrypted
on a line-by-line basis with each line having
the following format :
[<first 4 bytes store cipher-length>|<Next cipher-length
bytes store actual cipher>]
*/
static int encrypt_and_write_file(DYNAMIC_STRING *file_buf)
{
DBUG_ENTER("encrypt_and_write_file");
my_bool done= FALSE;
char cipher[MY_LINE_MAX], *tmp= NULL;
uint bytes_read=0, len= 0;
int enc_len= 0; // Can be negative.
if (reset_login_file(0) == -1)
goto error;
/* Move past key first. */
if (my_seek(g_fd, MY_LOGIN_HEADER_LEN, SEEK_SET, MYF(MY_WME))
!= (MY_LOGIN_HEADER_LEN))
goto error; /* Error while seeking. */
tmp= &file_buf->str[bytes_read];
while(! done)
{
len= 0;
while(*tmp++ != '\n')
if (len < (file_buf->length - bytes_read))
len ++;
else
{
done= TRUE;
break;
}
if (done)
break;
if ((enc_len= my_aes_get_size(len + 1, my_aes_128_ecb)) >
(MY_LINE_MAX - (int)MAX_CIPHER_STORE_LEN))
{
my_perror("A parameter to mysql_config_editor exceeds the maximum "
"accepted length. Please review the data you've supplied "
"and try to shorten them permissible length.\n");
goto error;
}
if (encrypt_buffer(&file_buf->str[bytes_read], ++len,
cipher + MAX_CIPHER_STORE_LEN, enc_len) < 0)
goto error;
bytes_read += len;
/* Store cipher length first. */
int4store(cipher, enc_len);
if ((my_write(g_fd, (const uchar *)cipher, enc_len + MAX_CIPHER_STORE_LEN,
MYF(MY_WME))) != (enc_len + MAX_CIPHER_STORE_LEN))
goto error;
}
verbose_msg("Successfully written encrypted data to the login file.\n");
/* Update file_size */
file_size= bytes_read;
DBUG_RETURN(0);
error:
my_perror("couldn't encrypt the file");
DBUG_RETURN(-1);
}
/**
Read the login file, unscramble its contents and store
them into the file buffer.
@param file_buf [in] Buffer for storing the unscrambled login
file contents.
@return -1 Error
0 Success
*/
static int read_and_decrypt_file(DYNAMIC_STRING *file_buf)
{
DBUG_ENTER("read_and_decrypt_file");
char cipher[MY_LINE_MAX], plain[MY_LINE_MAX];
uchar len_buf[MAX_CIPHER_STORE_LEN];
int cipher_len= 0, dec_len= 0;
/* Move past key first. */
if (my_seek(g_fd, MY_LOGIN_HEADER_LEN, SEEK_SET, MYF(MY_WME))
!= (MY_LOGIN_HEADER_LEN))
goto error; /* Error while seeking. */
/* First read the length of the cipher. */
while (my_read(g_fd, len_buf, MAX_CIPHER_STORE_LEN,
MYF(MY_WME)) == MAX_CIPHER_STORE_LEN)
{
cipher_len= sint4korr(len_buf);
if (cipher_len > MY_LINE_MAX)
goto error;
/* Now read 'cipher_len' bytes from the file. */
if ((int) my_read(g_fd, (uchar *) cipher, cipher_len, MYF(MY_WME)) == cipher_len)
{
if ((dec_len= decrypt_buffer(cipher, cipher_len, plain)) < 0)
goto error;
plain[dec_len]= 0;
dynstr_append(file_buf, plain);
}
}
verbose_msg("Successfully decrypted the login file.\n");
DBUG_RETURN(0);
error:
my_perror("couldn't decrypt the file");
DBUG_RETURN(-1);
}
/**
Encrypt the given plain text.
@param plain [in] Plain text to be encrypted.
@param plain_len [in] Length of the plain text.
@param cipher [out] Encrypted cipher text.
@return -1 if error encountered,
length encrypted, otherwise.
*/
static int encrypt_buffer(const char *plain, int plain_len, char cipher[], const int aes_len)
{
DBUG_ENTER("encrypt_buffer");
if (my_aes_encrypt((const unsigned char *) plain, plain_len,
(unsigned char *) cipher,
(const unsigned char *) my_key, LOGIN_KEY_LEN,
my_aes_128_ecb, NULL) == aes_len)
DBUG_RETURN(aes_len);
verbose_msg("Error! Couldn't encrypt the buffer.\n");
DBUG_RETURN(-1); /* Error */
}
/**
Decrypt the given cipher text.
@param cipher [in] Cipher text to be decrypted.
@param cipher_len [in] Length of the cipher text.
@param plain [out] Decrypted plain text.
@return -1 if error encountered,
length decrypted, otherwise.
*/
static int decrypt_buffer(const char *cipher, int cipher_len, char plain[])
{
DBUG_ENTER("decrypt_buffer");
int aes_length;
if ((aes_length= my_aes_decrypt((const unsigned char *) cipher, cipher_len,
(unsigned char *) plain,
(const unsigned char *) my_key,
LOGIN_KEY_LEN,
my_aes_128_ecb, NULL)) > 0)
DBUG_RETURN(aes_length);
verbose_msg("Error! Couldn't decrypt the buffer.\n");
DBUG_RETURN(-1); /* Error */
}
/**
Add unused bytes alongwith the to the login key
to the login file.
@return -1 if error encountered,
length written, otherwise.
*/
static int add_header(void)
{
DBUG_ENTER("add_header");
/* Reserved for future use. */
const char unused[]= {'\0','\0','\0','\0'};
/* Write 'unused' bytes first. */
if ((my_write(g_fd, (const uchar *) unused, 4, MYF(MY_WME))) != 4)
goto error;
/* Write the login key. */
if ((my_write(g_fd, (const uchar *)my_key, LOGIN_KEY_LEN, MYF(MY_WME)))
!= LOGIN_KEY_LEN)
goto error;
verbose_msg("Key successfully written to the file.\n");
DBUG_RETURN(MY_LOGIN_HEADER_LEN);
error:
my_perror("file write operation failed");
DBUG_RETURN(-1);
}
/**
Algorithm to generate key.
*/
void generate_login_key()
{
DBUG_ENTER("generate_login_key");
struct rand_struct rnd;
verbose_msg("Generating a new key.\n");
/* Get a sequence of random non-printable ASCII */
for (uint i= 0; i < LOGIN_KEY_LEN; i++)
my_key[i]= (char)((int)(my_rnd_ssl(&rnd) * 100000) % 32);
DBUG_VOID_RETURN;
}
/**
Read the stored login key.
@return -1 Error
0 Success
*/
static int read_login_key(void)
{
DBUG_ENTER("read_login_key");
verbose_msg("Reading the login key.\n");
/* Move past the unused buffer. */
if (my_seek(g_fd, 4, SEEK_SET, MYF(MY_WME)) != 4)
goto error; /* Error while seeking. */
if (my_read(g_fd, (uchar *)my_key, LOGIN_KEY_LEN, MYF(MY_WME))
!= LOGIN_KEY_LEN)
goto error; /* Error while reading. */
verbose_msg("Login key read successfully.\n");
DBUG_RETURN(0);
error:
my_perror("file read operation failed");
DBUG_RETURN(-1);
}
static void verbose_msg(const char *fmt, ...)
{
DBUG_ENTER("verbose_msg");
va_list args;
if (!opt_verbose)
DBUG_VOID_RETURN;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fflush(stderr);
DBUG_VOID_RETURN;
}
static void my_perror(const char *msg)
{
char errbuf[MYSYS_STRERROR_SIZE];
if (errno == 0)
fprintf(stderr, "%s\n", (msg) ? msg : "");
else
fprintf(stderr, "%s : %s\n", (msg) ? msg : "",
my_strerror(errbuf, sizeof(errbuf), errno));
// reset errno
errno= 0;
}
static void usage_command(int command)
{
print_version();
puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2012"));
puts("MySQL Configuration Utility.");
printf("\nDescription: %s\n", command_data[command].description);
printf("Usage: %s [program options] [%s [command options]]\n",
my_progname, command_data[command].name);
my_print_help(command_data[command].options);
my_print_variables(command_data[command].options);
}
static void usage_program(void)
{
print_version();
puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2012"));
puts("MySQL Configuration Utility.");
printf("Usage: %s [program options] [command [command options]]\n",
my_progname);
my_print_help(my_program_long_options);
my_print_variables(my_program_long_options);
puts("\nWhere command can be any one of the following :\n\
set [command options] Sets user name/password/host name/socket/port\n\
for a given login path (section).\n\
remove [command options] Remove a login path from the login file.\n\
print [command options] Print all the options for a specified\n\
login path.\n\
reset [command options] Deletes the contents of the login file.\n\
help Display this usage/help information.\n");
}
static void print_version(void) {
printf ("%s Ver %s Distrib %s, for %s on %s\n", my_progname,
MYSQL_CONFIG_EDITOR_VERSION, MYSQL_SERVER_VERSION,
SYSTEM_TYPE, MACHINE_TYPE);
}