延伸 MySQL 8.4  /  ...  /  編寫伺服器端身份驗證外掛程式

4.4.9.1 編寫伺服器端身份驗證外掛程式

使用適用於所有伺服器外掛程式類型的常用通用描述符格式宣告伺服器端外掛程式(請參閱第 4.4.2.1 節「伺服器外掛程式庫和外掛程式描述符」)。對於 auth_simple 外掛程式,描述符如下所示

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 成員 (auth_simple) 指示在語句中(例如 INSTALL PLUGINUNINSTALL PLUGIN)引用外掛程式時要使用的名稱。這也是 SHOW PLUGINSINFORMATION_SCHEMA.PLUGINS 顯示的名稱。

通用描述符的 auth_simple_handler 成員指向類型特定的描述符。對於身份驗證外掛程式,類型特定的描述符是 st_mysql_auth 結構的實例(在 plugin_auth.h 中定義)

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,類型特定的描述符如下所示

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 中找到的結構定義如下所示

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 成員,則表示字串的長度(以位元組為單位)。字串也是以 null 結尾的。

當伺服器調用身份驗證外掛程式時,它應該按如下方式解譯 MYSQL_SERVER_AUTH_INFO 結構成員。其中一些成員用於設定用戶端工作階段中 SQL 函式或系統變數的值,如所示。

  • user_name:客戶端發送的使用者名稱。該值會成為 USER() 函式值。

  • user_name_lengthuser_name 的長度(以位元組為單位)。

  • auth_string:在 mysql.user 系統表格中,與匹配的帳戶名稱對應的列的 authentication_string 的值(也就是說,與客戶端使用者名稱和主機名稱相符,且伺服器用來決定如何驗證客戶端的資料列)。

    假設您使用下列語句建立一個帳戶

    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 的值)。外掛程式可以變更它,以指示客戶端應具有不同使用者的權限。例如,如果外掛程式支援 Proxy 使用者,則初始值為連線(Proxy)使用者的名稱,而外掛程式可以將此成員變更為被 Proxy 的使用者名稱。然後,伺服器會將 Proxy 使用者視為具有被 Proxy 使用者的權限(假設 Proxy 使用者支援的其他條件都滿足;請參閱第 4.4.9.4 節「在身份驗證外掛程式中實作 Proxy 使用者支援」)。該值表示為最多 MYSQL_USER_NAME_LENGTH 位元組長度的字串,外加一個終止 null。該值會成為 CURRENT_USER() 函式值。

  • external_user:伺服器會將此設定為空字串(以 null 結尾)。它的值會成為 external_user 系統變數值。如果外掛程式希望該系統變數具有不同的值,則應據此設定此成員(例如,設定為連線的使用者名稱)。該值表示為最多 511 個位元組長度的字串,外加一個終止 null。

  • password_used:此成員適用於身份驗證失敗的情況。外掛程式可以設定它或忽略它。該值用於建構失敗錯誤訊息 Authentication fails. Password used: %spassword_used 的值決定了如何處理 %s,如下表所示。

    password_used %s 處理
    0 NO
    1 YES
    2 將沒有 %s
  • host_or_ip:如果可以解析,則為客戶端主機的名稱;否則為 IP 位址。

  • host_or_ip_lengthhost_or_ip 的長度(以位元組為單位)。

auth_simple 主要函式 auth_simple_server() 從客戶端讀取密碼(以 null 結尾的字串),如果密碼為非空(第一個位元組不是 null),則表示成功

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 表格中計算外掛程式錯誤。

auth_simple_server() 非常基本,除了設定指示是否收到密碼的成員之外,它不使用身份驗證資訊結構。

支援 Proxy 使用者的外掛程式必須將被 Proxy 的使用者名稱(也就是客戶端使用者應取得其權限的 MySQL 使用者)傳回伺服器。為此,外掛程式必須將 info->authenticated_as 成員設定為被 Proxy 的使用者名稱。關於 Proxy 的資訊,請參閱Proxy 使用者,以及第 4.4.9.4 節「在身份驗證外掛程式中實作 Proxy 使用者支援」

外掛程式描述符的 generate_authentication_string 成員會取得密碼,並從該密碼產生密碼雜湊值(摘要)。

  • 前兩個引數是指向輸出緩衝區及其最大長度(以位元組為單位)的指標。此函式應將密碼雜湊值寫入輸出緩衝區,並將長度重設為實際的雜湊值長度。

  • 後兩個引數指示密碼輸入緩衝區及其長度(以位元組為單位)。

  • 此函式在成功時傳回 0,若發生錯誤則傳回 1。

對於 auth_simple 外掛程式,generate_auth_string_hash() 函式會實作 generate_authentication_string 成員。它只會複製密碼,除非密碼太長而無法放入輸出緩衝區。

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 成員。它會無條件地傳回成功。

int validate_auth_string_hash(char* const inbuf  __attribute__((unused)),
                              unsigned int buflen  __attribute__((unused)))
{
  return 0;     /* success */
}

外掛程式描述符的 set_salt 成員僅由 mysql_native_password 外掛程式使用(請參閱原生可插拔身分驗證)。對於其他身分驗證外掛程式,您可以使用此簡易實作。

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權限,或是mysql資料庫的UPDATE權限。

  • AUTH_FLAG_USES_INTERNAL_STORAGE:外掛程式是否使用內部儲存空間(在 mysql.user 資料列的 authentication_string 欄中)。如果未設定此旗標,嘗試設定密碼將會失敗,且伺服器會產生警告。

  • AUTH_FLAG_REQUIRES_REGISTRATION:此旗標會針對需要註冊程序的身分驗證外掛程式設定。它會在 CREATE USERALTER USER 陳述式中,以及當 authentication_policy 系統變數被賦值時進行檢查。