西部数码主机 | 阿里云主机| 虚拟主机 | 服务器 | 返回乐道官网
当前位置: 主页 > 开发教程 > mysql教程 >

MySQL特性之mysql_config_editor源码解析

时间:2016-01-06 12:02来源:未知 作者:好模板 点击:
从mysql5.6开始,mysql推出了加密工具mysql_config_editor。在此之前我们通过将账号和密码明文放入my.cnf,从而使用mysql客户端登录时,无需指定账号密码就可以登录数据库。而有了mysql_config
从mysql5.6开始,mysql推出了加密工具mysql_config_editor。在此之前我们通过将账号和密码明文放入my.cnf,从而使用mysql客户端登录时,无需指定账号密码就可以登录数据库。而有了mysql_config_editor工具之后,我们将加密后的账号密码放入二进制文件。在登录时,客户端通过解密该文件来登录数据库。由于加密解密都在内存中进行,所以无法明文的显示文件内容。只要我们将文件权限保存好,就可以防止不怀好意的人解密我们的数据库密码了.

mysql_config_editor的使用过程如下:
mysql_config_editor set --login-path=client --host=localhost --user=localuser --password

这样我们就配置了一个为本地的数据源信息:
login-path :指定通过mysql客户端登录时的标识
host:我们要连接的数据库
user: 通过本地连接数据库时,使用的账号
password:指定通过本地连接时,使用的数据库密码(这里假设输入的密码为password1)

当然,如果通过远程连接,我们可能还要加上特定的端口信息。这样,当我们登录数据库时,只需要如下命令就可以连接到该数据库了:
mysql —login-path=client

这样我们就连接到本地数据库了。 
下面我们来看看mysql_config_editor的细节部分:
由于该工具包含set/remove/print/reset/help,所以我们仅分析set功能的实现:
set功能是通过set_command函数实现的,该函数主要用于配置账号密码等数据源信息,并将该信息存储到二进制文件:

点击(此处)折叠或打开

  1. static int set_command(void)
  2. {
  3. DBUG_ENTER("set_command");
  4.  
  5. DYNAMIC_STRING file_buf, path_buf;
  6. init_dynamic_string(&path_buf, "", MY_LINE_MAX, MY_LINE_MAX);
  7. init_dynamic_string(&file_buf, "", file_size, 3 * MY_LINE_MAX);
  8.  
  9. if (tty_password)
  10. opt_password= get_tty_password(NullS); 
  11. if (file_size)
  12. {
  13. if (read_and_decrypt_file(&file_buf) == -1) //如果文件存在,就读取文件,并将文件的密文解密后存放到file_buf中.
  14. goto error;
  15. }
  16.  
  17. dynstr_append(&path_buf, "["); /* --login=path */ 
  18. if (opt_login_path)
  19. dynstr_append(&path_buf, opt_login_path);
  20. else
  21. dynstr_append(&path_buf, "client");
  22. dynstr_append(&path_buf, "]");
  23.  
  24. if (opt_user) /* --user */
  25. {
  26. dynstr_append(&path_buf, "\nuser = ");
  27. dynstr_append(&path_buf, opt_user);
  28. }
  29.  
  30. if (opt_password) /* --password */
  31. {
  32. dynstr_append(&path_buf, "\npassword = ");
  33. dynstr_append(&path_buf, opt_password);
  34. }
  35.  
  36. if (opt_host) /* --host */
  37. {
  38. dynstr_append(&path_buf, "\nhost = ");
  39. dynstr_append(&path_buf, opt_host);
  40. }
  41.  
  42. if (opt_socket)
  43. {
  44. dynstr_append(&path_buf, "\nsocket = ");
  45. dynstr_append(&path_buf, opt_socket);
  46. }
  47.  
  48. if (opt_port)
  49. {
  50. dynstr_append(&path_buf, "\nport = ");
  51. dynstr_append(&path_buf, opt_port);
  52. }
  53.  
  54. dynstr_append(&path_buf, "\n");
  55.  
  56. /* Warn if login path already exists */
  57. if (opt_warn && ((locate_login_path (&file_buf, opt_login_path)) //判断该login-path是否已经存在
  58. != NULL))
  59. {
  60. int choice;
  61. printf ("WARNING : \'%s\' path already exists and will be "
  62. "overwritten. \n Continue? (Press y|Y for Yes, any "
  63. "other key for No) : ",
  64. opt_login_path);
  65. choice= getchar();
  66.  
  67. if (choice != (int) 'y' && choice != (int) 'Y’) //如果login-path存在是否选择覆盖
  68. goto done; /* skip */
  69. }
  70.  
  71. /* Remove the login path. */
  72. remove_login_path(&file_buf, opt_login_path); //从原来文件中读取的内容中,删掉该login-path信息
  73.  
  74. /* Append the new login path to the file buffer. */
  75. dynstr_append(&file_buf, path_buf.str); //将该login-path的信息加到file_buf的末尾
  76.  
  77. if (encrypt_and_write_file(&file_buf) == -1) //将包含新的log-path的所有信息和原来的信息加密写入文件
  78. goto error;
  79.  
  80. done:
  81. dynstr_free(&file_buf);
  82. dynstr_free(&path_buf);
  83. DBUG_RETURN(0);
  84.  
  85. error:
  86. dynstr_free(&file_buf);
  87. dynstr_free(&path_buf);
  88. DBUG_RETURN(-1);
  89. }

代码的具体逻辑如下:

 
 
在这里我们重点看看其中涉及的几个重要的函数:
read_and_decrypt_file (读取文件内容,并解密后放到动态字符缓冲中)
locate_login_path(判断该login-path是否已经存在)
remove_login_path(如果login-path存在,则删除该login-path)
dynstr_append(&file_buf, path_buf.str); 将新的login-path添加到file_buf 末尾
encrypt_and_write_file(&file_buf) 将file_buf中的信息解码后写入到文件中
 
首先我们来看看加密后的文件格式如下:



这里我们假设之前已经存在加密的文件了.
由于加密文件的前4个byte为’\0’,是未使用的,所以跳过解密环节。之后,紧接着的20个bytes是存放的对称加密算法的秘钥,而这部分内容在read_and_decrypt_file(read_login_key获取)调用之前已经读取取到,所以也要跳过。
因此read_and_decrypt_file的过程如下:

点击(此处)折叠或打开

  1. /*
  2. Header length for the login file.
  3. 4-byte (unused) + LOGIN_KEY_LEN
  4. */
  5. #define MY_LOGIN_HEADER_LEN (4 + LOGIN_KEY_LEN)
  6.  static int read_and_decrypt_file(DYNAMIC_STRING *file_buf)
  7. {
  8. DBUG_ENTER("read_and_decrypt_file");
  9.  
  10. char cipher[MY_LINE_MAX], plain[MY_LINE_MAX];
  11. uchar len_buf[MAX_CIPHER_STORE_LEN];
  12. int cipher_len= 0, dec_len= 0;
  13.  
  14. /* Move past key first. */
  15. if (my_seek(g_fd, MY_LOGIN_HEADER_LEN, SEEK_SET, MYF(MY_WME)) //跳过之前的unused bytes和login key部分
  16. != (MY_LOGIN_HEADER_LEN))
  17. goto error; /* Error while seeking. */
  18.  
  19. /* First read the length of the cipher. */
  20. while (my_read(g_fd, len_buf, MAX_CIPHER_STORE_LEN, //获取密文的长度
  21. MYF(MY_WME)) == MAX_CIPHER_STORE_LEN)
  22. {
  23. cipher_len= sint4korr(len_buf); //将密文的长度转换成整形
  24.  
  25. if (cipher_len > MY_LINE_MAX)
  26. goto error;
  27.  
  28. /* Now read 'cipher_len' bytes from the file. */
  29. if ((int) my_read(g_fd, (uchar *) cipher, cipher_len, MYF(MY_WME)) == cipher_len) //读取相应密文长度的密文
  30. {
  31. if ((dec_len= decrypt_buffer(cipher, cipher_len, plain)) < 0) //解密该密文
  32. goto error;
  33.  
  34. plain[dec_len]= 0;
  35. dynstr_append(file_buf, plain); //将解密后的密文追加到file_buf中
  36. }
  37. }
  38. verbose_msg("Successfully decrypted the login file.\n");
  39. DBUG_RETURN(0);
  40. error:
  41. my_perror("couldn't decrypt the file");
  42. DBUG_RETURN(-1);
  43. }

所以该函数的过程,就变为下面四个步骤的重复,只到文件中所有的密文都解密。这样,file_buf中就包含了所有的文件的明文信息:
1.获取密文的长度
2.根据获取的长度,读取文件中的密文
3.根据读取到的密文,进行解密
4.将解密后的内容,追加到file_buf缓冲区中。
 
在函数中,我们看到会将获取到的密文的长度,通过sint4korr转换,那是为什么呢 ? 
从上面我们可以知道,一个cipher其实有一个 4bytes的长度+cipher的字符串
所以,通过int4store 将cipher的长度存储在cipher字符串的前4个bytes中,通过sint4korr将cipher前4个bytes中的
值转化为实际的cipher长度:

点击(此处)折叠或打开

  1. #define int4store(T,A) do { *((char *)(T))=(char) ((A));\
  2. *(((char *)(T))+1)=(char) (((A) >> 8));\
  3. *(((char *)(T))+2)=(char) (((A) >> 16));\
  4. *(((char *)(T))+3)=(char) (((A) >> 24));\
  5. } while(0)
  6.  
  7. #define sint4korr(A) (int32) (((int32) ((uchar) (A)[0])) +\
  8. (((int32) ((uchar) (A)[1]) << 8)) +\
  9. (((int32) ((uchar) (A)[2]) << 16)) +\
  10. (((int32) ((int16) (A)[3]) << 24)))

接下来再看看locate_login_path函数的实现:

点击(此处)折叠或打开

  1. static char* locate_login_path(DYNAMIC_STRING *file_buf, const char *path_name)
  2. {
  3. DBUG_ENTER("locate_login_path");
  4.  
  5. char *addr= NULL;
  6. DYNAMIC_STRING dy_path_name;
  7.  
  8. init_dynamic_string(&dy_path_name, "", 512, 512); // 初始化dy_path_name动态字符串
  9.  
  10. //将dy_path_name 设置为[path_name]
  11. dynstr_append(&dy_path_name, "\n[“);       
  12. dynstr_append(&dy_path_name, path_name);
  13. dynstr_append(&dy_path_name, "]");
  14.  
  15. //检查第一个login-path是否就是要寻找的login-path
  16. /* First check if it is the very first login path. */
  17. if (file_buf->str == strstr(file_buf->str, dy_path_name.str + 1))
  18. addr= file_buf->str;
  19. /* If not, scan through the file. */
  20. else
  21. {
  22. addr= strstr(file_buf->str, dy_path_name.str);
  23. if (addr)
  24. addr ++; /* Move past '\n' */
  25. }
  26.  
  27. dynstr_free(&dy_path_name);
  28. DBUG_RETURN(addr);  //返回找到的login-path在file_buf的首地址
  29. }
该函数主要是寻找login-path是否能已经存在,如果已经存在,返回该login-path在file_buf中的首地址。
如果该login-path已经存在,那么我们可能会选择remove该login-path,然后在添加该login-path。

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

点击(此处)折叠或打开

  1. static void remove_login_path(DYNAMIC_STRING *file_buf, const char *path_name)
  2. {
  3. DBUG_ENTER("remove_login_path");
  4.  
  5. char *start=NULL, *end= NULL;
  6. int to_move, len, diff;
  7. if((start= locate_login_path(file_buf, path_name)) == NULL) //如果该login-path不存在,直接结束
  8. /* login path was not found, skip.. */
  9. goto done;
  10.  
  11. end= strstr(start, "\n[“);   // end为从start开始寻找,下一个login-path的起始位置
  12.  
  13. if (end)    //如果该login-path是file_buf中间的某一个login-path
  14. {
  15. end ++; /* Move past '\n' */
  16. len= ((diff= (start - end)) > 0) ? diff : - diff;
  17. to_move= file_buf->length - (end - file_buf->str);
  18. }
  19. else       //如果该login-path是该file_buf中最后一个log-path
  20. {
  21. *start= '\0';
  22. file_buf->length= ((diff= (file_buf->str - start)) > 0) ? diff : - diff;
  23. goto done;
  24. }
  25.  
  26. while(to_move —)   //将该login-path之后的login-path整体前移,覆盖move掉的login-path
  27. *(start ++)= *(end ++);
  28.  
  29. *start= '\0';
  30. file_buf->length -= len;
  31.  
  32. done:
  33. DBUG_VOID_RETURN;
  34. }

该函数主要是覆盖已经存在的login-path相关的字符串。
函数:dynstr_append(&file_buf, path_buf.str) ,将新添加的login-path内容,添加到file_buf的末尾。
 
最后来看看最重要,也是最核心的加密函数encrypt_and_write_file的实现:

点击(此处)折叠或打开

  1. static int encrypt_and_write_file(DYNAMIC_STRING *file_buf)
  2. {
  3. DBUG_ENTER("encrypt_and_write_file");
  4. my_bool done= FALSE;
  5. char cipher[MY_LINE_MAX], *tmp= NULL;
  6. uint bytes_read=0, len= 0;
  7. int enc_len= 0; // Can be negative.
  8.  
  9. if (reset_login_file(0) == -1) //清空文件,并重新生成随机加密秘钥,并将对称加密秘钥写入文件头部
  10. goto error;
  11. /* Move past key first. */
  12. if (my_seek(g_fd, MY_LOGIN_HEADER_LEN, SEEK_SET, MYF(MY_WME))
  13. != (MY_LOGIN_HEADER_LEN))
  14. goto error; /* Error while seeking. */
  15.  
  16. tmp= &file_buf->str[bytes_read];
  17. while(! done)
  18. {
  19. len= 0;
  20.  
  21. while(*tmp++ != '\n’) //读取file_buf中的每一行内容
  22. if (len < (file_buf->length - bytes_read))
  23. len ++;
  24. else
  25. {
  26. done= TRUE; 
  27. break;
  28. }
  29.  
  30. if (done)
  31. break;
  32.  
  33. if ((enc_len= encrypt_buffer(&file_buf->str[bytes_read],++ len, cipher + MAX_CIPHER_STORE_LEN)) < 0) //对读到的这一行内容进行加密,并将密文存放到cipher + MAX_CIPHER_STORE_LEN的地址处
  34. goto error;
  35.  
  36. bytes_read += len;
  37.  
  38. if (enc_len > MY_LINE_MAX)
  39. goto error;
  40.  
  41. /* Store cipher length first. */
  42. int4store(cipher, enc_len); //将密文的长度存放到cipher的头部
  43.  
  44. if ((my_write(g_fd, (const uchar *)cipher, enc_len + MAX_CIPHER_STORE_LEN,
  45. MYF(MY_WME))) != (enc_len + MAX_CIPHER_STORE_LEN)) //将该行加密过的密文写到文件
  46. goto error;
  47. }
  48. verbose_msg("Successfully written encrypted data to the login file.\n");
  49. /* Update file_size */
  50. file_size= bytes_read; //更新文件大小
  51.  
  52. DBUG_RETURN(0);
  53.  
  54. error:
  55. my_perror("couldn't encrypt the file");
  56. DBUG_RETURN(-1);
  57. }
 该函数主要功能如下:
  •   读取file_buf中一行
  •   对读取到的行,根据产生的KEY进行加密,将加密后的内容存放到cipher+MAX_CIPHER_STORE_LEN地址处
  •   将密文的长度存放到cipher和cipher+MAX_CIPHER_STORE_LEN之间的地址
  •   将cipher写入文件
  •   更新文件大小
 上述1~5一直循环至file_buf中的内容全部加密,并全部写入到文件中为止!

(责任编辑:好模板)
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
栏目列表
热点内容