MySQL new features-mysql_config_editor source code analysis
Mysql has launched the encryption tool mysql_config_editor since mysql5.6. Previously, we put the account and password in plain text into my. cnf, so that you can log on to the database without specifying the account and password when using the mysql client. With the mysql_config_editor tool, we put the encrypted account and password into the binary file. When you log on, the client decrypts the file to log on to the database. Since encryption and decryption are performed in the memory, the file content cannot be displayed in plaintext. As long as we keep the file permissions, we can prevent malicious people from decrypting our database password.
The usage of mysql_config_editor is as follows: mysql_config_editor set -- login-path = client -- host = localhost -- user = localuser -- password
In this way, we configure a local data source information: login-path: Specifies the identity host when logging on through the mysql client: the database user to be connected: When the database is connected through a local connection, account password used: Specifies the Database password used for local connection (assume that the password entered is password1)
Of course, if we use a remote connection, we may add specific port information. In this way, when we log on to the database, we only need the following command to connect to the database: mysql-login-path = client
Then we connect to the local database.
Next let's take a look at the details of mysql_config_editor: Because this tool contains set/remove/print/reset/help, we only analyze the implementation of the set function: the set function is implemented through the set_command function. This function is mainly used to configure data source information such as account and password, and store the information to a binary file:
- 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) // if the file exists, read the file, decrypt the ciphertext, and store it in file_buf.
- 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) // you can check whether the login-path already exists.
- ! = 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') // If the login-path already exists, do you want to overwrite it?
- Goto done;/* skip */
- }
- /* Remove the login path .*/
- Remove_login_path (& file_buf, opt_login_path); // Delete the login-path information from the content read from the original file.
- /* Append the new login path to the file buffer .*/
- Dynstr_append (& file_buf, path_buf.str); // Add the information of this login-path to the end of file_buf
- If (encrypt_and_write_file (& file_buf) =-1) // write all information containing the new log-path and the original information into the file encrypted
- 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 );
- }
The specific logic of the Code is as follows:
Here we will focus on several important functions involved: read_and_decrypt_file (reads the file content and decrypts it to the dynamic character buffer) locate_login_path (checks whether the login-path already exists) remove_login_path (if login-path exists, delete this login-path) dynstr_append (& file_buf, path_buf.str); Add the new login-path to the end of file_buf (& file_buf) decode the information in file_buf and write it to the file.
First, let's take a look at the encrypted file format as follows:
Here, we assume that an encrypted file already exists. Because the first four bytes of the encrypted file are '\ 0', they are not used, so the decryption process is skipped. Then, the next 20 bytes are the key of the stored symmetric encryption algorithm, and this part of the content has been read and obtained before the call of read_and_decrypt_file (read_login_key), So skip this step. Therefore, the read_and_decrypt_file process is as follows:
- /*
- Header length for the login file.
- 4-byte (unused) + LOGIN_KEY_LEN
- */
- # Define MY_LOGIN_HEADER_LEN (4 + LOGIN_KEY_LEN)
- 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) // skip the previous unused bytes and login key sections
- ! = (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, // obtain the ciphertext Length
- MYF (MY_WME) = MAX_CIPHER_STORE_LEN)
- {
- Cipher_len = sint4korr (len_buf); // converts the ciphertext length to an integer.
- 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) // read the ciphertext of the corresponding ciphertext Length
- {
- If (dec_len = decrypt_buffer (cipher, cipher_len, plain) <0) // decrypt the ciphertext
- Goto error;
- Plain [dec_len] = 0;
- Dynstr_append (file_buf, plain); // append the decrypted ciphertext to file_buf.
- }
- }
- Verbose_msg ("Successfully decrypted the login file. \ n ");
- DBUG_RETURN (0 );
- Error:
- My_perror ("couldn't decrypt the file ");
- DBUG_RETURN (-1 );
- }
Therefore, the process of this function becomes repeated in the following four steps, and only all the passwords in the file are decrypted. In this way, file_buf contains the plaintext information of all files: 1. obtain the ciphertext length. read the ciphertext in the file based on the obtained length. decrypt the data according to the read ciphertext. 4. append the decrypted content to the file_buf buffer.
In the function, we can see that the length of the obtained ciphertext is converted through sint4korr. Why? We can see from the above that a cipher actually has a 4 bytes length + cipher string. Therefore, the length of the cipher is stored in the first four bytes of the cipher string through int4store, use sint4korr to convert the values in the first four bytes of cipher to the actual cipher Length:
- #define int4store(T,A) do { *((char *)(T))=(char) ((A));\
- *(((char *)(T))+1)=(char) (((A) >> 8));\
- *(((char *)(T))+2)=(char) (((A) >> 16));\
- *(((char *)(T))+3)=(char) (((A) >> 24));\
- } while(0)
- #define sint4korr(A) (int32) (((int32) ((uchar) (A)[0])) +\
- (((int32) ((uchar) (A)[1]) << 8)) +\
- (((int32) ((uchar) (A)[2]) << 16)) +\
- (((int32) ((int16) (A)[3]) << 24)))
Next let's take a look at the implementation of the locate_login_path function:
- 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); // initialize the dynamic string dy_path_name
- // Set dy_path_name to [path_name]
- Dynstr_append (& dy_path_name, "\ n [");
- Dynstr_append (& dy_path_name, path_name );
- Dynstr_append (& dy_path_name, "]");
- // Check whether the First login-path is the login-path to be searched
- /* 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); // return the first address of the login-path in file_buf.
- }
This function is mainly used to check whether a login-path exists. If yes, the first address of the login-path in file_buf is returned.
If the login-path already exists, we may choose to remove the login-path and then add the login-path.
Next let's take a look at the implementation of removelogin-path:
- 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) // if the login-path does not exist, it ends directly.
- /* Login path was not found, skip ..*/
- Goto done;
- End = strstr (start, "\ n ["); // end is the starting position of the next login-path from start.
- If (end) // if the login-path is a login-path in the middle of file_buf
- {
- End ++;/* Move past '\ n '*/
- Len = (diff = (start-end)> 0 )? Diff:-diff;
- To_move = file_buf-> length-(end-file_buf-> str );
- }
- Else // If the login-path is the last log-path in the file_buf
- {
- * Start = '\ 0 ';
- File_buf-> length = (diff = (file_buf-> str-start)> 0 )? Diff:-diff;
- Goto done;
- }
- While (to_move-) // move the login-path after the login-path to overwrite the moved login-path
- * (Start ++) = * (end ++ );
- * Start = '\ 0 ';
- File_buf-> length-= len;
- Done:
- DBUG_VOID_RETURN;
- }
This function overwrites existing login-path strings. Function: dynstr_append (& file_buf, path_buf.str). Add the newly added login-path content to the end of file_buf.
Finally, let's take a look at the most important and core encryption function encrypt_and_write_file implementation:
- 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) // clear the file, regenerate the random encryption key, and write the symmetric encryption key to the file header.
- 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') // read the content of each row in file_buf
- If (len <(file_buf-> length-bytes_read ))
- Len ++;
- Else
- {
- Done = TRUE;
- Break;
- }
- If (done)
- Break;
- If (enc_len = encrypt_buffer (& file_buf-> str [bytes_read], ++ len, cipher + MAX_CIPHER_STORE_LEN) <0) // encrypts the content of the row, store the ciphertext at the address of cipher + MAX_CIPHER_STORE_LEN.
- Goto error;
- Bytes_read + = len;
- If (enc_len> MY_LINE_MAX)
- Goto error;
- /* Store cipher length first .*/
- Int4store (cipher, enc_len); // store the ciphertext length to the cipher header.
- If (my_write (g_fd, (const uchar *) cipher, enc_len + MAX_CIPHER_STORE_LEN,
- MYF (MY_WME )))! = (Enc_len + MAX_CIPHER_STORE_LEN) // write the encrypted ciphertext of the row to the file.
- Goto error;
- }
- Verbose_msg ("Successfully written encrypted data to the login file. \ n ");
- /* Update file_size */
- File_size = bytes_read; // update the file size
- DBUG_RETURN (0 );
- Error:
- My_perror ("couldn't encrypt the file ");
- DBUG_RETURN (-1 );
- }
This function provides the following functions:
- Read a row in file_buf
- Encrypt the read row based on the generated KEY and store the encrypted content at the cipher + MAX_CIPHER_STORE_LEN address.
- Store the ciphertext length between cipher and cipher + MAX_CIPHER_STORE_LEN.
- Write cipher to a file
- Update file size
Above 1 ~ 5 until all contents in file_buf are encrypted and all contents are written to the file!
The next section will introduce the specific encryption algorithm, and write a program to decrypt the file through the relevant decryption algorithm !!