MySQL新特性之mysql_config_editor源码解析_PHP教程
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函数实现的,该函数主要用于配置账号密码等数据源信息,并将该信息存储到二进制文件:
<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>
代码的具体逻辑如下:

在这里我们重点看看其中涉及的几个重要的函数: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的过程如下:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>/*<br /> </li><li>Header length for the login file.<br /></li><li>4-byte (unused) + LOGIN_KEY_LEN<br /></li><li>*/<br /></li><li>#define MY_LOGIN_HEADER_LEN (4 + LOGIN_KEY_LEN)<br /></li><li>static int read_and_decrypt_file(DYNAMIC_STRING *file_buf)<br /></li><li>{<br /></li><li>DBUG_ENTER("read_and_decrypt_file");<br /></li><li><br /></li><li>char cipher[MY_LINE_MAX], plain[MY_LINE_MAX];<br /></li><li>uchar len_buf[MAX_CIPHER_STORE_LEN];<br /></li><li>int cipher_len= 0, dec_len= 0;<br /></li><li><br /></li><li>/* Move past key first. */<br /></li><li>if (my_seek(g_fd, MY_LOGIN_HEADER_LEN, SEEK_SET, MYF(MY_WME)) //跳过之前的unused bytes和login key部分<br /></li><li>!= (MY_LOGIN_HEADER_LEN))<br /></li><li>goto error; /* Error while seeking. */<br /></li><li><br /></li><li>/* First read the length of the cipher. */<br /></li><li>while (my_read(g_fd, len_buf, MAX_CIPHER_STORE_LEN, //获取密文的长度<br /></li><li>MYF(MY_WME)) == MAX_CIPHER_STORE_LEN)<br /></li><li>{<br /></li><li>cipher_len= sint4korr(len_buf); //将密文的长度转换成整形<br /></li><li><br /></li><li>if (cipher_len > MY_LINE_MAX)<br /></li><li>goto error;<br /></li><li><br /></li><li>/* Now read 'cipher_len' bytes from the file. */<br /></li><li>if ((int) my_read(g_fd, (uchar *) cipher, cipher_len, MYF(MY_WME)) == cipher_len) //读取相应密文长度的密文<br /></li><li>{<br /></li><li>if ((dec_len= decrypt_buffer(cipher, cipher_len, plain)) < 0) //解密该密文<br /></li><li>goto error;<br /></li><li><br /></li><li>plain[dec_len]= 0;<br /></li><li>dynstr_append(file_buf, plain); //将解密后的密文追加到file_buf中<br /></li><li>}<br /></li><li>}<br /></li><li>verbose_msg("Successfully decrypted the login file.\n");<br /></li><li>DBUG_RETURN(0);<br /></li><li>error:<br /></li><li>my_perror("couldn't decrypt the file");<br /></li><li>DBUG_RETURN(-1);<br /></li><li>} </li></ol>
所以该函数的过程,就变为下面四个步骤的重复,只到文件中所有的密文都解密。这样,file_buf中就包含了所有的文件的明文信息:1.获取密文的长度2.根据获取的长度,读取文件中的密文3.根据读取到的密文,进行解密4.将解密后的内容,追加到file_buf缓冲区中。
在函数中,我们看到会将获取到的密文的长度,通过sint4korr转换,那是为什么呢 ?从上面我们可以知道,一个cipher其实有一个 4bytes的长度+cipher的字符串所以,通过int4store 将cipher的长度存储在cipher字符串的前4个bytes中,通过sint4korr将cipher前4个bytes中的值转化为实际的cipher长度:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>#define int4store(T,A) do { *((char *)(T))=(char) ((A));\<br /> </li><li>*(((char *)(T))+1)=(char) (((A) >> 8));\<br /></li><li>*(((char *)(T))+2)=(char) (((A) >> 16));\<br /></li><li>*(((char *)(T))+3)=(char) (((A) >> 24));\<br /></li><li>} while(0)<br /></li><li><br /></li><li>#define sint4korr(A) (int32) (((int32) ((uchar) (A)[0])) +\<br /></li><li>(((int32) ((uchar) (A)[1]) << 8)) +\<br /></li><li>(((int32) ((uchar) (A)[2]) << 16)) +\<br /></li><li>(((int32) ((int16) (A)[3]) << 24))) </li></ol>
接下来再看看locate_login_path函数的实现:
- 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); // 初始化dy_path_name动态字符串
- //将dy_path_name 设置为[path_name]
- dynstr_append(&dy_path_name, "\n[“);
- dynstr_append(&dy_path_name, path_name);
- dynstr_append(&dy_path_name, "]");
- //检查第一个login-path是否就是要寻找的login-path
- /* 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); //返回找到的login-path在file_buf的首地址
- }
如果该login-path已经存在,那么我们可能会选择remove该login-path,然后在添加该login-path。
接下来我们看看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) //如果该login-path不存在,直接结束
- /* login path was not found, skip.. */
- goto done;
- end= strstr(start, "\n[“); //end为从start开始寻找,下一个login-path的起始位置
- if (end) //如果该login-path是file_buf中间的某一个login-path
- {
- end ++; /* Move past '\n' */
- len= ((diff= (start - end)) > 0) ? diff : - diff;
- to_move= file_buf->length - (end - file_buf->str);
- }
- else //如果该login-path是该file_buf中最后一个log-path
- {
- *start= '\0';
- file_buf->length= ((diff= (file_buf->str - start)) > 0) ? diff : - diff;
- goto done;
- }
- while(to_move —) //将该login-path之后的login-path整体前移,覆盖move掉的login-path
- *(start ++)= *(end ++);
- *start= '\0';
- file_buf->length -= len;
- done:
- DBUG_VOID_RETURN;
- }
该函数主要是覆盖已经存在的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写入文件
- 更新文件大小
下一节会讲到具体采用的加密算法,并会通过相关的解密算法,编写程序对该文件进行解密操作!!

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas



Dalam beberapa hari kebelakangan ini, Ice Universe terus mendedahkan butiran mengenai Galaxy S25 Ultra, yang secara meluas dipercayai sebagai telefon pintar utama Samsung yang seterusnya. Antara lain, pembocor itu mendakwa bahawa Samsung hanya merancang untuk membawa satu peningkatan kamera

OnLeaks kini telah bekerjasama dengan Android Headlines untuk memberikan pandangan pertama pada Galaxy S25 Ultra, beberapa hari selepas percubaan gagal untuk menjana lebih daripada $4,000 daripada pengikut X (dahulunya Twitter). Untuk konteks, imej pemaparan yang dibenamkan di bawah h

Di samping mengumumkan dua telefon pintar baharu, TCL juga telah mengumumkan tablet Android baharu yang dipanggil NXTPAPER 14, dan saiz skrinnya yang besar adalah salah satu nilai jualannya. NXTPAPER 14 menampilkan versi 3.0 jenama tandatangan TCL panel LCD matte

Samsung belum menawarkan sebarang petunjuk lagi tentang bila ia akan mengemas kini siri telefon pintar Edisi Peminat (FE). Seperti sedia ada, Galaxy S23 FE kekal sebagai edisi terbaharu syarikat, telah dibentangkan pada awal Oktober 2023. Walau bagaimanapun, banyak

Vivo Y300 Pro baru sahaja didedahkan sepenuhnya, dan ia merupakan salah satu telefon Android jarak pertengahan paling tipis dengan bateri yang besar. Tepatnya, telefon pintar ini hanya tebal 7.69 mm tetapi mempunyai bateri 6,500 mAh. Ini adalah kapasiti yang sama seperti yang dilancarkan baru-baru ini

Dalam beberapa hari kebelakangan ini, Ice Universe terus mendedahkan butiran mengenai Galaxy S25 Ultra, yang secara meluas dipercayai sebagai telefon pintar utama Samsung yang seterusnya. Antara lain, pembocor itu mendakwa bahawa Samsung hanya merancang untuk membawa satu peningkatan kamera

Redmi Note 14 Pro Plus kini rasmi sebagai pengganti langsung kepada Redmi Note 13 Pro Plus tahun lepas (sekira $375 di Amazon). Seperti yang dijangkakan, Redmi Note 14 Pro Plus mengetuai siri Redmi Note 14 bersama Redmi Note 14 dan Redmi Note 14 Pro. Li

Jenama OnePlus iQOO mempunyai kitaran produk 2023-4 yang mungkin hampir tamat; Namun begitu, jenama itu telah mengisytiharkan bahawa ia belum selesai dengan siri Z9nya. Varian Turbo+yang terakhir, dan mungkin paling akhir, baru sahaja diumumkan seperti yang diramalkan. T
