Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> MySQL新特性之mysql_config_editor源碼解析

MySQL新特性之mysql_config_editor源碼解析

編輯:關於android開發

MySQL新特性之mysql_config_editor源碼解析


從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. DYNAMIC_STRING file_buf, path_buf;
  5. init_dynamic_string(&path_buf, "", MY_LINE_MAX, MY_LINE_MAX);
  6. init_dynamic_string(&file_buf, "", file_size, 3 * MY_LINE_MAX);

  7. if (tty_password)
  8. opt_password= get_tty_password(NullS);
  9. if (file_size)
  10. {
  11. if (read_and_decrypt_file(&file_buf) == -1) //如果文件存在,就讀取文件,並將文件的密文解密後存放到file_buf中.
  12. goto error;
  13. }

  14. dynstr_append(&path_buf, "["); /* --login=path */
  15. if (opt_login_path)
  16. dynstr_append(&path_buf, opt_login_path);
  17. else
  18. dynstr_append(&path_buf, "client");
  19. dynstr_append(&path_buf, "]");

  20. if (opt_user) /* --user */
  21. {
  22. dynstr_append(&path_buf, "\nuser = ");
  23. dynstr_append(&path_buf, opt_user);
  24. }

  25. if (opt_password) /* --password */
  26. {
  27. dynstr_append(&path_buf, "\npassword = ");
  28. dynstr_append(&path_buf, opt_password);
  29. }

  30. if (opt_host) /* --host */
  31. {
  32. dynstr_append(&path_buf, "\nhost = ");
  33. dynstr_append(&path_buf, opt_host);
  34. }

  35. if (opt_socket)
  36. {
  37. dynstr_append(&path_buf, "\nsocket = ");
  38. dynstr_append(&path_buf, opt_socket);
  39. }

  40. if (opt_port)
  41. {
  42. dynstr_append(&path_buf, "\nport = ");
  43. dynstr_append(&path_buf, opt_port);
  44. }

  45. dynstr_append(&path_buf, "\n");

  46. /* Warn if login path already exists */
  47. if (opt_warn && ((locate_login_path (&file_buf, opt_login_path)) //判斷該login-path是否已經存在
  48. != NULL))
  49. {
  50. int choice;
  51. printf ("WARNING : \'%s\' path already exists and will be "
  52. "overwritten. \n Continue? (Press y|Y for Yes, any "
  53. "other key for No) : ",
  54. opt_login_path);
  55. choice= getchar();

  56. if (choice != (int) 'y' && choice != (int) 'Y’) //如果login-path存在是否選擇覆蓋
  57. goto done; /* skip */
  58. }

  59. /* Remove the login path. */
  60. remove_login_path(&file_buf, opt_login_path); //從原來文件中讀取的內容中,刪掉該login-path信息

  61. /* Append the new login path to the file buffer. */
  62. dynstr_append(&file_buf, path_buf.str); //將該login-path的信息加到file_buf的末尾

  63. if (encrypt_and_write_file(&file_buf) == -1) //將包含新的log-path的所有信息和原來的信息加密寫入文件
  64. goto error;

  65. done:
  66. dynstr_free(&file_buf);
  67. dynstr_free(&path_buf);
  68. DBUG_RETURN(0);

  69. error:
  70. dynstr_free(&file_buf);
  71. dynstr_free(&path_buf);
  72. DBUG_RETURN(-1);
  73. }

代碼的具體邏輯如下:


在這裡我們重點看看其中涉及的幾個重要的函數: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. char cipher[MY_LINE_MAX], plain[MY_LINE_MAX];
  10. uchar len_buf[MAX_CIPHER_STORE_LEN];
  11. int cipher_len= 0, dec_len= 0;

  12. /* Move past key first. */
  13. if (my_seek(g_fd, MY_LOGIN_HEADER_LEN, SEEK_SET, MYF(MY_WME)) //跳過之前的unused bytes和login key部分
  14. != (MY_LOGIN_HEADER_LEN))
  15. goto error; /* Error while seeking. */

  16. /* First read the length of the cipher. */
  17. while (my_read(g_fd, len_buf, MAX_CIPHER_STORE_LEN, //獲取密文的長度
  18. MYF(MY_WME)) == MAX_CIPHER_STORE_LEN)
  19. {
  20. cipher_len= sint4korr(len_buf); //將密文的長度轉換成整形

  21. if (cipher_len > MY_LINE_MAX)
  22. goto error;

  23. /* Now read 'cipher_len' bytes from the file. */
  24. if ((int) my_read(g_fd, (uchar *) cipher, cipher_len, MYF(MY_WME)) == cipher_len) //讀取相應密文長度的密文
  25. {
  26. if ((dec_len= decrypt_buffer(cipher, cipher_len, plain)) < 0) //解密該密文
  27. goto error;

  28. plain[dec_len]= 0;
  29. dynstr_append(file_buf, plain); //將解密後的密文追加到file_buf中
  30. }
  31. }
  32. verbose_msg("Successfully decrypted the login file.\n");
  33. DBUG_RETURN(0);
  34. error:
  35. my_perror("couldn't decrypt the file");
  36. DBUG_RETURN(-1);
  37. }

所以該函數的過程,就變為下面四個步驟的重復,只到文件中所有的密文都解密。這樣,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. #define sint4korr(A) (int32) (((int32) ((uchar) (A)[0])) +\
  7. (((int32) ((uchar) (A)[1]) << 8)) +\
  8. (((int32) ((uchar) (A)[2]) << 16)) +\
  9. (((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. 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的實現:

  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. if (reset_login_file(0) == -1) //清空文件,並重新生成隨機加密秘鑰,並將對稱加密秘鑰寫入文件頭部
  9. goto error;
  10. /* Move past key first. */
  11. if (my_seek(g_fd, MY_LOGIN_HEADER_LEN, SEEK_SET, MYF(MY_WME))
  12. != (MY_LOGIN_HEADER_LEN))
  13. goto error; /* Error while seeking. */

  14. tmp= &file_buf->str[bytes_read];
  15. while(! done)
  16. {
  17. len= 0;

  18. while(*tmp++ != '\n’) //讀取file_buf中的每一行內容
  19. if (len < (file_buf->length - bytes_read))
  20. len ++;
  21. else
  22. {
  23. done= TRUE;
  24. break;
  25. }

  26. if (done)
  27. break;

  28. if ((enc_len= encrypt_buffer(&file_buf->str[bytes_read],++len,cipher+MAX_CIPHER_STORE_LEN))<0) //對讀到的這一行內容進行加密,並將密文存放到cipher + MAX_CIPHER_STORE_LEN的地址處
  29. goto error;

  30. bytes_read += len;

  31. if (enc_len > MY_LINE_MAX)
  32. goto error;

  33. /* Store cipher length first. */
  34. int4store(cipher, enc_len); //將密文的長度存放到cipher的頭部

  35. if ((my_write(g_fd, (const uchar *)cipher, enc_len + MAX_CIPHER_STORE_LEN,
  36. MYF(MY_WME))) != (enc_len + MAX_CIPHER_STORE_LEN)) //將該行加密過的密文寫到文件
  37. goto error;
  38. }
  39. verbose_msg("Successfully written encrypted data to the login file.\n");
  40. /* Update file_size */
  41. file_size= bytes_read; //更新文件大小

  42. DBUG_RETURN(0);

  43. error:
  44. my_perror("couldn't encrypt the file");
  45. DBUG_RETURN(-1);
  46. }
該函數主要功能如下:
  • 讀取file_buf中一行
  • 對讀取到的行,根據產生的KEY進行加密,將加密後的內容存放到cipher+MAX_CIPHER_STORE_LEN地址處
  • 將密文的長度存放到cipher和cipher+MAX_CIPHER_STORE_LEN之間的地址
  • 將cipher寫入文件
  • 更新文件大小
上述1~5一直循環至file_buf中的內容全部加密,並全部寫入到文件中為止!
下一節會講到具體采用的加密算法,並會通過相關的解密算法,編寫程序對該文件進行解密操作!!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved