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写入文件
- 更新文件大小
下一节会讲到具体采用的加密算法,并会通过相关的解密算法,编写程序对该文件进行解密操作!!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











최근 아이스 유니버스는 삼성의 차기 플래그십 스마트폰으로 널리 알려진 갤럭시 S25 울트라에 대한 세부 정보를 꾸준히 공개해 왔습니다. 무엇보다도 유출자는 삼성이 카메라 업그레이드를 하나만 가져올 계획이라고 주장했습니다.

OnLeaks는 이제 Android Headlines와 제휴하여 X(이전 Twitter) 팔로어로부터 4,000달러 이상의 수익을 창출하려는 시도가 실패한 지 며칠 후 Galaxy S25 Ultra에 대한 첫 번째 모습을 제공합니다. 맥락에 따라 h 아래에 포함된 렌더링 이미지

TCL은 두 가지 새로운 스마트폰을 발표하는 것과 함께 NXTPAPER 14라는 새로운 Android 태블릿도 발표했는데, TCL의 거대한 화면 크기는 판매 포인트 중 하나입니다. NXTPAPER 14는 TCL의 시그니처 브랜드인 무광택 LCD 패널 버전 3.0을 갖추고 있습니다.

삼성전자는 팬에디션(FE) 스마트폰 시리즈를 언제 업데이트할지 아직 힌트를 주지 않았다. 현재 상태로 Galaxy S23 FE는 2023년 10월 초에 출시된 회사의 최신 버전으로 남아 있습니다.

Vivo Y300 Pro는 방금 완전히 공개되었으며 대용량 배터리를 갖춘 가장 얇은 중급 Android 휴대폰 중 하나입니다. 정확히 말하면 스마트폰의 두께는 7.69mm에 불과하지만 배터리 용량은 6,500mAh입니다. 최근 출시된 것과 동일한 용량이다.

최근 아이스 유니버스는 삼성의 차기 플래그십 스마트폰으로 널리 알려진 갤럭시 S25 울트라에 대한 세부 정보를 꾸준히 공개해 왔습니다. 무엇보다도 유출자는 삼성이 카메라 업그레이드를 하나만 가져올 계획이라고 주장했습니다.

Redmi Note 14 Pro Plus는 이제 작년 Redmi Note 13 Pro Plus(Amazon에서 현재 $375)의 직접적인 후속 제품으로 공식화되었습니다. 예상대로 Redmi Note 14 Pro Plus는 Redmi Note 14 및 Redmi Note 14 Pro와 함께 Redmi Note 14 시리즈를 주도합니다. 리

OnePlus의 자매 브랜드 iQOO는 2023-4년 제품 주기가 거의 끝날 수 있습니다. 그럼에도 불구하고 브랜드는 Z9 시리즈가 아직 끝나지 않았다고 선언했습니다. 최종이자 아마도 최고급인 Turbo+ 변형이 예상대로 발표되었습니다. 티
