暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL · 源码分析 · 连接与认证过程

ZzzMickey 2024-03-29
215

Author: 西加

前言

本文主要介绍 client 与 server 连接与认证过程,包括client和server之间的网络交互情况

使用 mysql 5.6.16官方版本分析

认证流程分析

client 建立连接的认证过程如下图

  1. server 监听端口
  2. client 向server建立TCP连接
  3. server 向client发送挑战码报文(报文详细内容在下文中有分析)
  4. client 使用挑战码加密密码,将加密后的密码包含在回包中,发送给server
  5. server 根据client的回包,验证密码的有效性,给client发送ok包或error包
    client 给 server 的回包,和 server 的ok包内容在下面的代码栈里有体现

server 给 client 的挑战码报文下下文中有详细分析

image.png

和其他c语言调用方式一样,mysql client 也是调用 mysql_read_connect 建立连接

mysql_real_connect 实际是 client.c 里面的 CLI_MYSQL_REAL_CONNECT 函数

server监听请求

main mysqld_main handle_connections_sockets /* 监听socket请求 */ poll mysql_socket_accept create_new_thread

client 向 server 建立连接请求

main sql_connect sql_read_connect mysql_read_connect vio_socket_connect inline_mysql_socket_connect /*向server发送连接请求*/ connect /* 等待server回包 */ cli_safe_read my_net_read /* client 主要认证过程 */ run_plugin_auth native_password_auth_client /* 使用挑战码加密密码 */ scramble client_mpvio_write_packet /* 拼接client的返回包,包含: 1. client 能力位 2. 最大报文长度 3. 字符集 4. 用户名 5. 挑战码加密后的密码 6. database name (如果能力位包含 CLIENT_CONNECT_WITH_DB 7. client auth plugin name (如果能力位包含 CLIENT_PLUGIN_AUTH */ send_client_reply_packet /* 发送认证包 */ my_net_write
handle_one_connection do_handle_one_connection thd_prepare_connection login_connection check_connection /* 检查host或IP是否能匹配某个用户 */ acl_check_host acl_authenticate do_auth_once native_password_authenticate /*给 client 发送挑战码*/ server_mpvio_write_packet /* 发送 handshake 包 */ send_server_handshake_packet /* */ server_mpvio_read_packet /* 接收 client 报文 */ my_net_read /* 解析client报文 */ parse_client_handshake_packet /* 检查密码有效性 */ check_scramble Protocol::end_statement Protocol::send_ok /* 拼接 ok 报文,包含: 1. 0 标志位,占一字节 2. affected_rows 3. last_insert_id 4. server_status 5. warning_count 6. message 建立连接的时候,一般只有 server_status 有内容,其他都是0或者空 */ net_send_ok /* 发送 ok 报文 */ my_net_write

挑战码报文

挑战码是一段随机字符串,server 发送给client用于加密client输入的密码,client给server的回包中包含挑战码加密后的密码,从而避免了网络传输明文密码

static bool send_server_handshake_packet(MPVIO_EXT *mpvio, const char *data, uint data_len) { /* 申请 1 + 60 + 20 + 64 = 145 byte */ char *buff= (char *) my_alloca(1 + SERVER_VERSION_LENGTH + data_len + 64); char scramble_buf[SCRAMBLE_LENGTH]; /* end 是写报文的指针*/ char *end= buff; /* 报文第一位写 protocol_version */ *end++= protocol_version; /* 第二位开始写 server_version 笔者编译的时候带了suffix,version一共24字节,以5.6.16开头 加'/0'一共25字节 */ end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1; /* 4个字节保存 thread_id */ int4store((uchar*) end, mpvio->thread_id); end+= 4; /* 发送头8个字节挑战码,用于兼容旧版客户端 后12个字节写在后面,会被旧版客户端忽略 */ end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323); end+= SCRAMBLE_LENGTH_323; /* 第一段挑战码结尾 */ *end++= 0; /* 保存低16位能力位 能力位作用见官方文档: https://dev.mysql.com/doc/internals/en/capability-flags.html */ int2store(end, mpvio->client_capabilities); /* charset 信息 */ end[2]= (char) default_charset_info->number; /* server_status server_status 是 automatic, in trans 等状态信息 每个位的含义在 include/mysql_com.h 中,以 SERVER_STATUS_ 开头的宏定义 */ int2store(end + 3, mpvio->server_status[0]); /* 能力位的高16位 */ int2store(end + 5, mpvio->client_capabilities >> 16); /* 挑战码长度 */ end[7]= data_len; DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;); /* 10字节0,应该是保留位 */ memset(end + 8, 0, 10); /* 高低共4字节能力位,1字节charset,2字节server_status,1字节挑战码长度,10字节0,共18位 */ end+= 18; /* 其余部分的挑战码 */ end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323, data_len - SCRAMBLE_LENGTH_323); end+= data_len - SCRAMBLE_LENGTH_323; /* auth plugin name,一般是 mysql_native_password,21字节 */ end= strmake(end, plugin_name(mpvio->plugin)->str, plugin_name(mpvio->plugin)->length);
文章转载自ZzzMickey,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论