Extending MySQL 9.0  /  ...  /  Writing the Server-Side Authentication Plugin

4.4.9.1 Writing the Server-Side Authentication Plugin

Declare the server-side plugin with the usual general descriptor format that is used for all server plugin types (see Section 4.4.2.1, “Server Plugin Library and Plugin Descriptors”). For the auth_simple plugin, the descriptor looks like this:

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;

The name member (auth_simple) indicates the name to use for references to the plugin in statements such as INSTALL PLUGIN or UNINSTALL PLUGIN. This is also the name displayed by SHOW PLUGINS or INFORMATION_SCHEMA.PLUGINS.

The auth_simple_handler member of the general descriptor points to the type-specific descriptor. For an authentication plugin, the type-specific descriptor is an instance of the st_mysql_auth structure (defined in 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;
};

The st_mysql_auth structure has these members:

  • interface_version: The type-specific API version number, always MYSQL_AUTHENTICATION_INTERFACE_VERSION

  • client_auth_plugin: The client plugin name

  • authenticate_user: A pointer to the main plugin function that communicates with the client

  • generate_authentication_string: A pointer to a plugin function that generates a password digest from an authentication string

  • validate_authentication_string: A pointer to a plugin function that validates a password digest

  • set_salt: A pointer to a plugin function that converts a scrambled password to binary form

  • authentication_flags: A flags word

The client_auth_plugin member should indicate the name of the client plugin if a specific plugin is required. A value of NULL means any plugin. In the latter case, whatever plugin the client uses will do. This is useful if the server plugin does not care about the client plugin or what user name or password it sends. For example, this might be true if the server plugin authenticates only local clients and uses some property of the operating system rather than the information sent by the client plugin.

For auth_simple, the type-specific descriptor looks like this:

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
};

The main function, auth_simple_server(), takes two arguments representing an I/O structure and a MYSQL_SERVER_AUTH_INFO structure. The structure definition, found in plugin_auth.h, looks like this:

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;

The character set for string members is UTF-8. If there is a _length member associated with a string, it indicates the string length in bytes. Strings are also null-terminated.

When an authentication plugin is invoked by the server, it should interpret the MYSQL_SERVER_AUTH_INFO structure members as follows. Some of these are used to set the value of SQL functions or system variables within the client session, as indicated.

  • user_name: The user name sent by the client. The value becomes the USER() function value.

  • user_name_length: The length of user_name in bytes.

  • auth_string: The value of the authentication_string column of the row in the mysql.user system table for the matching account name (that is, the row that matches the client user name and host name and that the server uses to determine how to authenticate the client).

    Suppose that you create an account using the following statement:

    CREATE USER 'my_user'@'localhost'
      IDENTIFIED WITH my_plugin AS 'my_auth_string';

    When my_user connects from the local host, the server invokes my_plugin and passes 'my_auth_string' to it as the auth_string value.

  • auth_string_length: The length of auth_string in bytes.

  • authenticated_as: The server sets this to the user name (the value of user_name). The plugin can alter it to indicate that the client should have the privileges of a different user. For example, if the plugin supports proxy users, the initial value is the name of the connecting (proxy) user, and the plugin can change this member to the proxied user name. The server then treats the proxy user as having the privileges of the proxied user (assuming that the other conditions for proxy user support are satisfied; see Section 4.4.9.4, “Implementing Proxy User Support in Authentication Plugins”). The value is represented as a string at most MYSQL_USER_NAME_LENGTH bytes long, plus a terminating null. The value becomes the CURRENT_USER() function value.

  • external_user: The server sets this to the empty string (null terminated). Its value becomes the external_user system variable value. If the plugin wants that system variable to have a different value, it should set this member accordingly (for example, to the connecting user name). The value is represented as a string at most 511 bytes long, plus a terminating null.

  • password_used: This member applies when authentication fails. The plugin can set it or ignore it. The value is used to construct the failure error message of Authentication fails. Password used: %s. The value of password_used determines how %s is handled, as shown in the following table.

    password_used %s Handling
    0 NO
    1 YES
    2 There will be no %s
  • host_or_ip: The name of the client host if it can be resolved, or the IP address otherwise.

  • host_or_ip_length: The length of host_or_ip in bytes.

The auth_simple main function, auth_simple_server(), reads the password (a null-terminated string) from the client and succeeds if the password is nonempty (first byte not 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;
}

The main function should return one of the error codes shown in the following table.

Error Code Meaning
CR_OK Success
CR_OK_HANDSHAKE_COMPLETE Do not send a status packet back to client
CR_ERROR Error
CR_AUTH_USER_CREDENTIALS Authentication failure
CR_AUTH_HANDSHAKE Authentication handshake failure
CR_AUTH_PLUGIN_ERROR Internal plugin error

For an example of how the handshake works, see the plugin/auth/dialog.c source file.

The server counts plugin errors in the Performance Schema host_cache table.

auth_simple_server() is so basic that it does not use the authentication information structure except to set the member that indicates whether a password was received.

A plugin that supports proxy users must return to the server the name of the proxied user (the MySQL user whose privileges the client user should get). To do this, the plugin must set the info->authenticated_as member to the proxied user name. For information about proxying, see Proxy Users, and Section 4.4.9.4, “Implementing Proxy User Support in Authentication Plugins”.

The generate_authentication_string member of the plugin descriptor takes the password and generates a password hash (digest) from it:

  • The first two arguments are pointers to the output buffer and its maximum length in bytes. The function should write the password hash to the output buffer and reset the length to the actual hash length.

  • The second two arguments indicate the password input buffer and its length in bytes.

  • The function returns 0 for success, 1 if an error occurred.

For the auth_simple plugin, the generate_auth_string_hash() function implements the generate_authentication_string member. It just makes a copy of the password, unless it is too long to fit in the output buffer.

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 */
}

The validate_authentication_string member of the plugin descriptor validates a password hash:

  • The arguments are a pointer to the password hash and its length in bytes.

  • The function returns 0 for success, 1 if the password hash cannot be validated.

For the auth_simple plugin, the validate_auth_string_hash() function implements the validate_authentication_string member. It returns success unconditionally:

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

The set_salt member of the plugin descriptor was used only by the mysql_native_password plugin, which is no longer supported in MySQL 9.0. For other authentication plugins, you can use this trivial implementation:

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 */
}

The authentication_flags member of the plugin descriptor contains flags that affect plugin operation. The permitted flags are:

  • AUTH_FLAG_PRIVILEGED_USER_FOR_PASSWORD_CHANGE: Credential changes are a privileged operation. If this flag is set, the server requires that the user has the global CREATE USER privilege or the UPDATE privilege for the mysql database.

  • AUTH_FLAG_USES_INTERNAL_STORAGE: Whether the plugin uses internal storage (in the authentication_string column of mysql.user rows). If this flag is not set, attempts to set the password fail and the server produces a warning.

  • AUTH_FLAG_REQUIRES_REGISTRATION: This flag is set for authentication plugins that require a registration process. It is checked for CREATE USER and ALTER USER statements, and when the authentication_policy system variable is assigned a value.