MySQL · 源码分析 · mysql认证阶段漫游

client发起一个连接请求, 到拿到server返回的ok包之间, 走三次握手, 交换了[不可告人]的验证信息, 这期间mysql如何完成校验工作?

过程(三次握手)

信息是如何加密的

client:

hash_stage1 = sha1(password)
hash_stage2 = sha1(hash_stage1)
reply = sha1(scramble, hash_stage2) ^ hash_stage1

server: (逻辑位于sql/password.c:check_scramble_sha1中, 下文亦有提及)

// mysql.user表中, 对应user的passwd实际上是hash_stage2
res1 = sha1(scramble, hash_stage2)
hash_stage1 = reply ^ res1
hash_stage2_reassured = sha1(hash_stage1)
再根据hash_stage2_reassured == hash_stage2(from mysql.user)是否一致来判定是否合法

涉事函数们

如图, client发起连接请求, server创建新的线程, 并进入acl_authenticate(5.7位于sql/auth/sql_authentication.cc, 5.6位于sql/sql_acl.cc)函数完成信息验证, 并把包里读出的信息更新到本线程.

流程堆栈:

#0  parse_client_handshake_packet
#1  server_mpvio_read_packet
#2  native_password_authenticate
#3  do_auth_once
#4  acl_authenticate
#5  check_connection
#6  login_connection
#7  thd_prepare_connection
#8  do_handle_one_connection

接下来考察这些函数中做了哪些事.
check_connection(sql/sql_connect.cc)
当接收到client的建连接请求时, 进入check_connection, 先对连接本身上下文分析(socket, tcp/ip的v4/6 哇之类的)
当然你用very long的host连进来, 也会在这里被cut掉防止overflow.
不合法的ip/host也会在这里直接返回, 如果环境ok, 就进入到acl_authenticate的逻辑中
acl_authenticate:
初始化MPVIO_EXT, 用于保存验证过程的上下文; 字符集, 挑战码, …的坑位, 上锁, 根据command进行分派, (新建链接为COM_CONNECT

COM_CONNECT下会进入函数do_auth_once(), 返回值直接决定握手成功与否.
先对authentication plugin做判定, 咱们这里基于”mysql_native_password”的情况

if (plugin)
  {
    st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
    res= auth->authenticate_user(mpvio, &mpvio->auth_info);
    ...

在mysql_native_password时会进入native_password_authenticate 逻辑:

  /* generate the scramble, or reuse the old one */
  if (mpvio->scramble[SCRAMBLE_LENGTH])
    create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);

  /* send it to the client */
  if (mpvio->write_packet(mpvio, (uchar*) mpvio->scramble, SCRAMBLE_LENGTH + 1))
    DBUG_RETURN(CR_AUTH_HANDSHAKE);

  /* read the reply with the encrypted password */
  if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
    DBUG_RETURN(CR_AUTH_HANDSHAKE);
  DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));

可见这里才生成了挑战码并发送到client, 再调用mpvio->read_packet等待client回包,
进入server_mpvio_read_packet,
这里的实现则调用常见的my_net_read读包,
当拿到auth包时, 逻辑分派到parse_client_handshake_packet, 对包内容进行parse, 这里会根据不同client protocol, 去掉头和尾, 还对client是否设置了ssl做判定. 接着:

  if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
  {
    /*
      Get the password field.
    */
    passwd= get_length_encoded_string(&end, &bytes_remaining_in_packet,
                                      &passwd_len);
  }
  else
  {
    /*
      Old passwords are zero terminatedtrings.
    */
    passwd= get_string(&end, &bytes_remaining_in_packet, &passwd_len);
  }
  ...

在拿到了client发来的加密串(虽然叫passwd), 暂时存放在内存中, 返回native_password_authenticate,
当判定为需要做password check时(万一有人不设置密码呢), 进入check_scramble, 这个函数中才实现了对密码的验证:

// server decode回包中的加密信息
// 把上面提到的三个公式包在函数中
my_bool
check_scramble_sha1(const uchar *scramble_arg, const char *message,
                    const uint8 *hash_stage2)
{
  uint8 buf[SHA1_HASH_SIZE];
  uint8 hash_stage2_reassured[SHA1_HASH_SIZE];

  /* create key to encrypt scramble */
  compute_sha1_hash_multi(buf, message, SCRAMBLE_LENGTH,
                          (const char *) hash_stage2, SHA1_HASH_SIZE);
  /* encrypt scramble */
  my_crypt((char *) buf, buf, scramble_arg, SCRAMBLE_LENGTH);

  /* now buf supposedly contains hash_stage1: so we can get hash_stage2 */
  compute_sha1_hash(hash_stage2_reassured, (const char *) buf, SHA1_HASH_SIZE);

  return MY_TEST(memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE));
}

native_password_authenticate拿到check_scamble的返回值, 返回OK,
再返回到acl_authenticate, 讲mpvio中环境信息更新到线程信息THD中, successful login~

(所以可以魔改这块代码搞事, 密码什么的, 权限什么的….
(我就说说, 别当真

时间: 2024-09-17 04:17:50

MySQL · 源码分析 · mysql认证阶段漫游的相关文章

MySQL · 源码分析 · MySQL 半同步复制数据一致性分析

简介 MySQL Replication为MySQL用户提供了高可用性和可扩展性解决方案.本文介绍了MySQL Replication的主要发展历程,然后通过三个参数rpl_semi_sync_master_wait_point.sync_binlog.sync_relay_log的配置简要分析了MySQL半同步的数据一致性. MySQL Replication的发展 在2000年,MySQL 3.23.15版本引入了Replication.Replication作为一种准实时同步方式,得到广泛

MySQL · 源码分析 · MySQL BINLOG半同步复制数据安全性分析

半同步复制(semisynchronous replication)MySQL使用广泛的数据复制方案,相比于MySQL内置的异步复制它保证了数据的安 全,本文从主机在Server层提交事务开始一直到主机确认收到备机回复进行一步步解析,来看MySQL的半同步复制是怎么保证数 据安全的.本文基于MySQL 5.6源码,为了简化本文只分析DML的核心的事务处理过程,并假定事务只涉及innodb存储引擎. MySQL的事务提交流程 在MySQL中事务的提交Server层最后会调用函数ha_commit_

MySQL · 源码分析 · MySQL replication partial transaction

replication 概述 目前MySQL支持的replication方式多种多样 普通的master-slave 异步replication 半同步的semi-sync replication 支持多通道的group replication和double binlog 如果按连接协议来区分,又可以分为 非GTID模式,通过binlog文件名和文件的偏移来决定replication位点信息 GTID模式,通过GTID信息来决定replication位点信息 如果按apply binglog的方

MySQL · 源码分析 · Innodb 引擎Redo日志存储格式简介

MySQL有多种日志.不同种类.不同目的的日志会记录在不同的日志文件中,它们可以帮助你找出mysqld内部发生的事情.比如错误日志:用来记录启动.运行或停止mysqld进程时出现的问题:查询日志:记录建立的客户端连接和执行的语句:二进制日志:记录所有更改数据的语句,主要用于逻辑复制:慢日志:记录所有执行时间超过long_query_time秒的所有查询或不使用索引的查询.而对MySQL中最常用的事务引擎innodb,redo日志是保证事务一致性非常重要的.本文结合MySQL版本5.6为分析源码介

MySQL · 源码分析 · InnoDB 异步IO工作流程

之前的一篇内核月报InnoDB IO子系统 中介绍了InnoDB IO子系统中包含的同步IO以及异步IO.本篇文章将从源码层面剖析一下InnoDB IO子系统中,数据页的同步IO以及异步IO请求的具体实现过程. 在MySQL5.6中,InnoDB的异步IO主要是用来处理预读以及对数据文件的写请求的.而对于正常的页面数据读取则是通过同步IO进行的.到底二者在代码层面上的实现过程有什么样的区别? 接下来我们将以Linux native io的执行过程为主线,对IO请求的执行过程进行梳理. 重点数据结

MySQL · 源码分析 · InnoDB LRU List刷脏改进之路

之前的一篇内核月报MySQL · 引擎特性 · InnoDB Buffer Pool 中对InnoDB Buffer pool的整体进行了详细的介绍.文章已经提到了LRU List以及刷脏的工作原理.本篇文章着重从MySQL 5.7源码层面对LRU List刷脏的工作原理,以及Percona针对MySQL LRU Flush的一些性能问题所做的改进,进行一下分析. 在MySQL中,如果当前数据库需要操作的数据集比Buffer pool中的空闲页面大的话,当前Buffer pool中的数据页就必须

MySQL · 源码分析 · 网络通信模块浅析

MySQL 网络通信浅析 MySQL的网络通信协议主要包含以下几个层次,从最上层的MySQL数据包协议层到最底层的socket传输: | THD | Protocol | NET | VIO | SOCKET 本文主要扫一下相关的代码,以下分析基于MySQL5.7. 创建会话 在MySQL5.7中对会话协议层的代码进行了大量的重构以优化性能,并使得代码更加可读.以下这幅图大概展示了几个相关的类关系(未包含诸如windows平台的相关类) 创建用户线程堆栈是从主线程开始的,监听客户端请求并创建处理

MySQL · 源码分析 · SHUTDOWN过程

ORACLE 中的SHUTDOWN MySQL SHUTDOWN LEVEL 暂时只有一种,源码中留了 LEVEL 的坑还没填 在此借用 Oracle 的 SHUTDOWN LEVEL 分析 Oracle SHUTDOWN LEVEL 共有四种:ABORT.IMMEDIATE.NORMAL.TRANSACTIONAL ABORT 立即结束所有SQL 回滚未提交事务 断开所有用户连接 下次启动实例时,需要recovery IMMEDIATE 允许正在运行的SQL执行完毕 回滚未提交事务 断开所有用

MySQL · 源码分析 · 一条insert语句的执行过程

本文只分析了insert语句执行的主路径,和路径上部分关键函数,很多细节没有深入,留给读者继续分析 create table t1(id int); insert into t1 values(1) 略过建立连接,从 mysql_parse() 开始分析 void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state) { /* ...... */ /* 检查query_cache,如果结果存在于c