Home  >  Article  >  Backend Development  >  MySQL new features: mysql_config_editor source code analysis_PHP tutorial

MySQL new features: mysql_config_editor source code analysis_PHP tutorial

WBOY
WBOYOriginal
2016-07-12 09:01:491223browse

MySQL new feature: mysql_config_editor source code analysis

Starting from mysql5.6, mysql has launched the encryption tool mysql_config_editor. Before this, we put the account and password in plain text into my.cnf, so that when logging in using the mysql client, we can log in to the database without specifying the account and password. With the mysql_config_editor tool, we put the encrypted account password into a binary file. On login, the client logs into the database by decrypting the file. Since encryption and decryption are performed in memory, the file contents cannot be displayed in plain text. As long as we keep the file permissions well, we can prevent malicious people from decrypting our database password.

The usage process 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: Specify the identity when logging in through the mysql client host: the database we want to connect to user: through local When connecting to the database, use the account password: Specify the database password used when connecting through a local connection (here it is assumed that the entered password is password1)

Of course, if connecting through a remote connection, we may also add a specific port information. In this way, when we log in to the database, we only need the following command to connect to the database: mysql --login-path=client
In this way, we will connect to the local database.
Let’s take a look at the details of mysql_config_editor: Since 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, which is mainly used Configure data source information such as account and password, and store the information in a binary file:

<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>static int set_command(void)<br /> </li><li>{<br /></li><li>DBUG_ENTER("set_command");<br /></li><li><br /></li><li>DYNAMIC_STRING file_buf, path_buf;<br /></li><li>init_dynamic_string(&path_buf, "", MY_LINE_MAX, MY_LINE_MAX);<br /></li><li>init_dynamic_string(&file_buf, "", file_size, 3 * MY_LINE_MAX);<br /></li><li><br /></li><li>if (tty_password)<br /></li><li>opt_password= get_tty_password(NullS); <br /></li><li>if (file_size)<br /></li><li>{<br /></li><li>if (read_and_decrypt_file(&file_buf) == -1) //如果文件存在,就读取文件,并将文件的密文解密后存放到file_buf中.<br /></li><li>goto error;<br /></li><li>}<br /></li><li><br /></li><li>dynstr_append(&path_buf, "["); /* --login=path */ <br /></li><li>if (opt_login_path)<br /></li><li>dynstr_append(&path_buf, opt_login_path);<br /></li><li>else<br /></li><li>dynstr_append(&path_buf, "client");<br /></li><li>dynstr_append(&path_buf, "]");<br /></li><li><br /></li><li>if (opt_user) /* --user */<br /></li><li>{<br /></li><li>dynstr_append(&path_buf, "\nuser = ");<br /></li><li>dynstr_append(&path_buf, opt_user);<br /></li><li>}<br /></li><li><br /></li><li>if (opt_password) /* --password */<br /></li><li>{<br /></li><li>dynstr_append(&path_buf, "\npassword = ");<br /></li><li>dynstr_append(&path_buf, opt_password);<br /></li><li>}<br /></li><li><br /></li><li>if (opt_host) /* --host */<br /></li><li>{<br /></li><li>dynstr_append(&path_buf, "\nhost = ");<br /></li><li>dynstr_append(&path_buf, opt_host);<br /></li><li>}<br /></li><li><br /></li><li>if (opt_socket)<br /></li><li>{<br /></li><li>dynstr_append(&path_buf, "\nsocket = ");<br /></li><li>dynstr_append(&path_buf, opt_socket);<br /></li><li>}<br /></li><li><br /></li><li>if (opt_port)<br /></li><li>{<br /></li><li>dynstr_append(&path_buf, "\nport = ");<br /></li><li>dynstr_append(&path_buf, opt_port);<br /></li><li>}<br /></li><li><br /></li><li>dynstr_append(&path_buf, "\n");<br /></li><li><br /></li><li>/* Warn if login path already exists */<br /></li><li>if (opt_warn && ((locate_login_path (&file_buf, opt_login_path)) //判断该login-path是否已经存在<br /></li><li>!= NULL))<br /></li><li>{<br /></li><li>int choice;<br /></li><li>printf ("WARNING : \'%s\' path already exists and will be "<br /></li><li>"overwritten. \n Continue? (Press y|Y for Yes, any "<br /></li><li>"other key for No) : ",<br /></li><li>opt_login_path);<br /></li><li>choice= getchar();<br /></li><li><br /></li><li>if (choice != (int) 'y' && choice != (int) 'Y’) //如果login-path存在是否选择覆盖<br /></li><li>goto done; /* skip */<br /></li><li>}<br /></li><li><br /></li><li>/* Remove the login path. */<br /></li><li>remove_login_path(&file_buf, opt_login_path); //从原来文件中读取的内容中,删掉该login-path信息<br /></li><li><br /></li><li>/* Append the new login path to the file buffer. */<br /></li><li>dynstr_append(&file_buf, path_buf.str); //将该login-path的信息加到file_buf的末尾<br /></li><li><br /></li><li>if (encrypt_and_write_file(&file_buf) == -1) //将包含新的log-path的所有信息和原来的信息加密写入文件<br /></li><li>goto error;<br /></li><li><br /></li><li>done:<br /></li><li>dynstr_free(&file_buf);<br /></li><li>dynstr_free(&path_buf);<br /></li><li>DBUG_RETURN(0);<br /></li><li><br /></li><li>error:<br /></li><li>dynstr_free(&file_buf);<br /></li><li>dynstr_free(&path_buf);<br /></li><li>DBUG_RETURN(-1);<br /></li><li>} </li></ol>

The specific logic of the code is as follows:


Here we focus on several important functions involved: read_and_decrypt_file (read the file content and decrypt it and put it in the dynamic character buffer) locate_login_path (determine whether the login-path already exists) remove_login_path (if login -path exists, delete the login-path) dynstr_append(&file_buf, path_buf.str); Add the new login-path to the end of file_buf encrypt_and_write_file(&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 before. Since the first 4 bytes of the encrypted file are'
  1. static char* locate_login_path(DYNAMIC_STRING *file_buf, const char *path_name)
  2. {
  3. DBUG_ENTER("locate_login_path");

  4. char *addr= NULL;
  5. DYNAMIC_STRING dy_path_name;

  6. init_dynamic_string(&dy_path_name, "", 512, 512); // 初始化dy_path_name动态字符串

  7. //将dy_path_name 设置为[path_name]
  8. dynstr_append(&dy_path_name, "\n[“);
  9. dynstr_append(&dy_path_name, path_name);
  10. dynstr_append(&dy_path_name, "]");

  11. //检查第一个login-path是否就是要寻找的login-path
  12. /* First check if it is the very first login path. */
  13. if (file_buf->str == strstr(file_buf->str, dy_path_name.str + 1))
  14. addr= file_buf->str;
  15. /* If not, scan through the file. */
  16. else
  17. {
  18. addr= strstr(file_buf->str, dy_path_name.str);
  19. if (addr)
  20. addr ++; /* Move past '\n' */
  21. }

  22. dynstr_free(&dy_path_name);
  23. DBUG_RETURN(addr); //返回找到的login-path在file_buf的首地址
  24. }
该函数主要是寻找login-path是否能已经存在,如果已经存在,返回该login-path在file_buf中的首地址。
如果该login-path已经存在,那么我们可能会选择remove该login-path,然后在添加该login-path。

接下来我们看看removelogin-path的实现:

  1. static void remove_login_path(DYNAMIC_STRING *file_buf, const char *path_name)
  2. {
  3. DBUG_ENTER("remove_login_path");

  4. char *start=NULL, *end= NULL;
  5. int to_move, len, diff;
  6. if((start= locate_login_path(file_buf, path_name)) == NULL) //如果该login-path不存在,直接结束
  7. /* login path was not found, skip.. */
  8. goto done;

  9. end= strstr(start, "\n[“); //end为从start开始寻找,下一个login-path的起始位置

  10. if (end) //如果该login-path是file_buf中间的某一个login-path
  11. {
  12. end ++; /* Move past '\n' */
  13. len= ((diff= (start - end)) > 0) ? diff : - diff;
  14. to_move= file_buf->length - (end - file_buf->str);
  15. }
  16. else //如果该login-path是该file_buf中最后一个log-path
  17. {
  18. *start= '\0';
  19. file_buf->length= ((diff= (file_buf->str - start)) > 0) ? diff : - diff;
  20. goto done;
  21. }

  22. while(to_move —) //将该login-path之后的login-path整体前移,覆盖move掉的login-path
  23. *(start ++)= *(end ++);

  24. *start= '\0';
  25. file_buf->length -= len;

  26. done:
  27. DBUG_VOID_RETURN;
  28. }

该函数主要是覆盖已经存在的login-path相关的字符串。 函数:dynstr_append(&file_buf, path_buf.str) ,将新添加的login-path内容,添加到file_buf的末尾。

最后来看看最重要,也是最核心的加密函数encrypt_and_write_file的实现:

<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>static int encrypt_and_write_file(DYNAMIC_STRING *file_buf)<br /> </li><li>{<br /></li><li>DBUG_ENTER("encrypt_and_write_file");<br /></li><li>my_bool done= FALSE;<br /></li><li>char cipher[MY_LINE_MAX], *tmp= NULL;<br /></li><li>uint bytes_read=0, len= 0;<br /></li><li>int enc_len= 0; // Can be negative.<br /></li><li><br /></li><li>if (reset_login_file(0) == -1) //清空文件,并重新生成随机加密秘钥,并将对称加密秘钥写入文件头部<br /></li><li>goto error;<br /></li><li>/* Move past key first. */<br /></li><li>if (my_seek(g_fd, MY_LOGIN_HEADER_LEN, SEEK_SET, MYF(MY_WME))<br /></li><li>!= (MY_LOGIN_HEADER_LEN))<br /></li><li>goto error; /* Error while seeking. */<br /></li><li><br /></li><li>tmp= &file_buf->str[bytes_read];<br /></li><li>while(! done)<br /></li><li>{<br /></li><li>len= 0;<br /></li><li><br /></li><li>while(*tmp++ != '\n’) //读取file_buf中的每一行内容<br /></li><li>if (len < (file_buf->length - bytes_read))<br /></li><li>len ++;<br /></li><li>else<br /></li><li>{<br /></li><li>done= TRUE; <br /></li><li>break;<br /></li><li>}<br /></li><li><br /></li><li>if (done)<br /></li><li>break;<br /></li><li><br /></li><li>if ((enc_len= encrypt_buffer(&file_buf->str[bytes_read],++len,cipher+MAX_CIPHER_STORE_LEN))<0) //对读到的这一行内容进行加密,并将密文存放到cipher + MAX_CIPHER_STORE_LEN的地址处</li><li>goto error;<br /></li><li><br /></li><li>bytes_read += len;<br /></li><li><br /></li><li>if (enc_len > MY_LINE_MAX)<br /></li><li>goto error;<br /></li><li><br /></li><li>/* Store cipher length first. */<br /></li><li>int4store(cipher, enc_len); //将密文的长度存放到cipher的头部<br /></li><li><br /></li><li>if ((my_write(g_fd, (const uchar *)cipher, enc_len + MAX_CIPHER_STORE_LEN,<br /></li><li>MYF(MY_WME))) != (enc_len + MAX_CIPHER_STORE_LEN)) //将该行加密过的密文写到文件<br /></li><li>goto error;<br /></li><li>}<br /></li><li>verbose_msg("Successfully written encrypted data to the login file.\n");<br /></li><li>/* Update file_size */<br /></li><li>file_size= bytes_read; //更新文件大小<br /></li><li><br /></li><li>DBUG_RETURN(0);<br /></li><li><br /></li><li>error:<br /></li><li>my_perror("couldn't encrypt the file");<br /></li><li>DBUG_RETURN(-1);<br /></li><li>} </li></ol>
该函数主要功能如下:
  • 读取file_buf中一行
  • 对读取到的行,根据产生的KEY进行加密,将加密后的内容存放到cipher+MAX_CIPHER_STORE_LEN地址处
  • 将密文的长度存放到cipher和cipher+MAX_CIPHER_STORE_LEN之间的地址
  • 将cipher写入文件
  • 更新文件大小
上述1~5一直循环至file_buf中的内容全部加密,并全部写入到文件中为止!
下一节会讲到具体采用的加密算法,并会通过相关的解密算法,编写程序对该文件进行解密操作!!

www.bkjia.comtruehttp://www.bkjia.com/PHPjc/1088144.htmlTechArticleMySQL新特性之mysql_config_editor源码解析 从mysql5.6开始,mysql推出了加密工具mysql_config_editor。在此之前我们通过将账号和密码明文放入my.cnf,从...
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn