扩展 MySQL 8.0  / 第 4 章 MySQL 插件 API  / 4.4 编写插件  / 4.4.9 编写认证插件  /  4.4.9.1 编写服务端认证插件

4.4.9.1 编写服务端认证插件

使用用于所有服务器插件类型的常用通用描述符格式声明服务器端插件(请参阅第 4.4.2.1 节,“服务器插件库和插件描述符”)。对于 auth_simple插件,描述符如下所示:

Press CTRL+C to copy
mysql_declare_plugin(auth_simple) { MYSQL_AUTHENTICATION_PLUGIN, &auth_simple_handler, /* type-specific descriptor */ "auth_simple", /* plugin name */ "Author Name", /* author */ "Any-password authentication plugin", /* description */ PLUGIN_LICENSE_GPL, /* license type */ NULL, /* no init function */ NULL, /* no deinit function */ 0x0100, /* version = 1.0 */ NULL, /* no status variables */ NULL, /* no system variables */ NULL, /* no reserved information */ 0 /* no flags */ } mysql_declare_plugin_end;

name成员 ( ) 表示在or auth_simple等​​语句中用于引用插件的 名称。这也是或 显示的名称。 INSTALL PLUGINUNINSTALL PLUGINSHOW PLUGINSINFORMATION_SCHEMA.PLUGINS

通用描述符的auth_simple_handler成员指向特定类型的描述符。对于身份验证插件,特定于类型的描述符是st_mysql_auth 结构的一个实例(在中定义plugin_auth.h):

Press CTRL+C to copy
struct st_mysql_auth { int interface_version; const char *client_auth_plugin; int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info); int (*generate_authentication_string)(char *outbuf, unsigned int *outbuflen, const char *inbuf, unsigned int inbuflen); int (*validate_authentication_string)(char* const inbuf, unsigned int buflen); int (*set_salt)(const char *password, unsigned int password_len, unsigned char* salt, unsigned char *salt_len); const unsigned long authentication_flags; };

st_mysql_auth结构具有以下成员:

  • interface_version: 类型特定的 API 版本号,总是 MYSQL_AUTHENTICATION_INTERFACE_VERSION

  • client_auth_plugin: 客户端插件名称

  • authenticate_user: 指向与客户端通信的主要插件函数的指针

  • generate_authentication_string: 指向从身份验证字符串生成密码摘要的插件函数的指针

  • validate_authentication_string: 指向验证密码摘要的插件函数的指针

  • set_salt: 指向将加扰密码转换为二进制形式的插件函数的指针

  • authentication_flags: 标志词

client_auth_plugin如果需要特定插件, 该成员应指明客户端插件的名称。值NULL表示 任何插件。在后一种情况下,客户端使用的任何插件都可以。如果服务器插件不关心客户端插件或它发送的用户名或密码,这很有用。例如,如果服务器插件仅对本地客户端进行身份验证并使用操作系统的某些属性而不是客户端插件发送的信息,则可能是这样。

对于auth_simple,特定于类型的描述符如下所示:

Press CTRL+C to copy
static struct st_mysql_auth auth_simple_handler = { MYSQL_AUTHENTICATION_INTERFACE_VERSION, "auth_simple", /* required client-side plugin name */ auth_simple_server /* server-side plugin main function */ generate_auth_string_hash, /* generate digest from password string */ validate_auth_string_hash, /* validate password digest */ set_salt, /* generate password salt value */ AUTH_FLAG_PRIVILEGED_USER_FOR_PASSWORD_CHANGE };

主函数auth_simple_server()有两个参数,分别代表一个 I/O 结构和一个 MYSQL_SERVER_AUTH_INFO结构。在 中找到的结构定义 plugin_auth.h如下所示:

Press CTRL+C to copy
typedef struct st_mysql_server_auth_info { char *user_name; unsigned int user_name_length; const char *auth_string; unsigned long auth_string_length; char authenticated_as[MYSQL_USERNAME_LENGTH+1]; char external_user[512]; int password_used; const char *host_or_ip; unsigned int host_or_ip_length; } MYSQL_SERVER_AUTH_INFO;

字符串成员的字符集是 UTF-8。如果有 _length与字符串相关联的成员,则表示字符串长度(以字节为单位)。字符串也是空终止的。

当服务器调用身份验证插件时,它应该 MYSQL_SERVER_AUTH_INFO按如下方式解释结构成员。如所示,其中一些用于在客户端会话中设置 SQL 函数或系统变量的值。

  • user_name:客户端发送的用户名。该值成为 USER()函数值。

  • user_name_lengthuser_name:以字节 为单位的长度 。

  • auth_string: 系统表中匹配账户名authentication_string的行的列值mysql.user(即匹配客户端用户名和主机名的行,服务器使用它来确定如何对客户端进行身份验证)。

    假设您使用以下语句创建一个帐户:

    Press CTRL+C to copy
    CREATE USER 'my_user'@'localhost' IDENTIFIED WITH my_plugin AS 'my_auth_string';

    my_user从本地主机连接时,服务器调用并作为值 my_plugin 传递 给它。'my_auth_string'auth_string

  • auth_string_lengthauth_string:以字节 为单位的长度 。

  • authenticated_as: 服务器将其设置为用户名( 的值 user_name)。该插件可以更改它以指示客户端应具有不同用户的权限。例如,如果插件支持代理用户,则初始值为连接(代理)用户的名称,插件可以将此成员更改为代理用户名。然后服务器将代理用户视为具有被代理用户的特权(假设满足代理用户支持的其他条件;请参阅 第 4.4.9.4 节,“在身份验证插件中实现代理用户支持”). 该值表示为最多 MYSQL_USER_NAME_LENGTH字节长的字符串,外加一个终止空值。该值成为 CURRENT_USER()函数值。

  • external_user:服务器将其设置为空字符串(以 null 结尾)。它的值成为external_user 系统变量值。如果插件希望该系统变量具有不同的值,它应该相应地设置该成员(例如,连接用户名)。该值表示为最多 511 个字节长的字符串,外加一个终止空值。

  • password_used:该成员在认证失败时应用。该插件可以设置它或忽略它。该值用于构造 的失败错误消息Authentication fails. Password used: %s。的值 password_used决定了如何 %s处理,如下表所示。

    password_used %s处理
    0
    1个 是的
    2个 将没有%s
  • host_or_ip: 如果可以解析,则为客户端主机的名称,否则为 IP 地址。

  • host_or_ip_lengthhost_or_ip:以字节 为单位的长度 。

auth_simple函数 , auth_simple_server()从客户端读取密码(一个以 null 结尾的字符串),如果密码为非空(第一个字节不为空)则成功:

Press CTRL+C to copy
static int auth_simple_server (MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) { unsigned char *pkt; int pkt_len; /* read the password as null-terminated string, fail on error */ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) return CR_ERROR; /* fail on empty password */ if (!pkt_len || *pkt == '\0') { info->password_used= PASSWORD_USED_NO; return CR_ERROR; } /* accept any nonempty password */ info->password_used= PASSWORD_USED_YES; return CR_OK; }

主函数应返回下表中显示的错误代码之一。

错误代码 意义
CR_OK 成功
CR_OK_HANDSHAKE_COMPLETE 不要将状态包发送回客户端
CR_ERROR 错误
CR_AUTH_USER_CREDENTIALS 验证失败
CR_AUTH_HANDSHAKE 认证握手失败
CR_AUTH_PLUGIN_ERROR 内部插件错误

有关握手如何工作的示例,请参阅 plugin/auth/dialog.c源文件。

host_cache服务器计算 Performance Schema表 中的插件错误 。

auth_simple_server()非常基础,除了设置指示是否收到密码的成员外,它不使用身份验证信息结构。

支持代理用户的插件必须向服务器返回代理用户的名称(客户端用户应获得其特权的 MySQL 用户)。为此,插件必须将 info->authenticated_as成员设置为代理用户名。有关代理的信息,请参阅 代理用户第 4.4.9.4 节,“在身份验证插件中实现代理用户支持”

插件描述符的generate_authentication_string成员获取密码并从中生成密码哈希(摘要):

  • 前两个参数是指向输出缓冲区的指针及其以字节为单位的最大长度。该函数应将密码哈希写入输出缓冲区并将长度重置为实际哈希长度。

  • 后两个参数指示密码输入缓冲区及其长度(以字节为单位)。

  • 该函数返回 0 表示成功,如果发生错误则返回 1。

对于auth_simple插件, generate_auth_string_hash()函数实现 generate_authentication_string成员。它只是复制密码,除非它太长而无法放入输出缓冲区。

Press CTRL+C to copy
int generate_auth_string_hash(char *outbuf, unsigned int *buflen, const char *inbuf, unsigned int inbuflen) { /* fail if buffer specified by server cannot be copied to output buffer */ if (*buflen < inbuflen) return 1; /* error */ strncpy(outbuf, inbuf, inbuflen); *buflen= strlen(inbuf); return 0; /* success */ }

插件描述符的validate_authentication_string成员验证密码哈希:

  • 参数是指向密码散列及其长度(以字节为单位)的指针。

  • 该函数返回 0 表示成功,如果无法验证密码哈希则返回 1。

对于auth_simple插件, validate_auth_string_hash()函数实现 validate_authentication_string成员。它无条件返回成功:

Press CTRL+C to copy
int validate_auth_string_hash(char* const inbuf __attribute__((unused)), unsigned int buflen __attribute__((unused))) { return 0; /* success */ }

插件描述符的set_salt成员仅由 mysql_native_password插件使用(请参阅 Native Pluggable Authentication)。对于其他身份验证插件,您可以使用这个简单的实现:

Press CTRL+C to copy
int set_salt(const char* password __attribute__((unused)), unsigned int password_len __attribute__((unused)), unsigned char* salt __attribute__((unused)), unsigned char* salt_len) { *salt_len= 0; return 0; /* success */ }

插件描述符的authentication_flags成员包含影响插件操作的标志。允许的标志是:

  • AUTH_FLAG_PRIVILEGED_USER_FOR_PASSWORD_CHANGE: 凭据更改是特权操作。如果设置此标志,则服务器要求用户具有全局CREATE USER 权限或数据库 UPDATE 权限。mysql

  • AUTH_FLAG_USES_INTERNAL_STORAGE:插件是否使用内部存储(在 authentication_string行列中 mysql.user)。如果未设置此标志,则尝试设置密码会失败并且服务器会发出警告。

  • AUTH_FLAG_REQUIRES_REGISTRATION:此标志是为需要注册过程的身份验证插件设置的。它检查 CREATE USERALTER USER语句,以及何时为 authentication_policy 系统变量赋值。在 MySQL 8.0.27 中添加。