Rechercher dans le manuel MySQL
29.2.4.9 Writing Authentication Plugins
MySQL supports pluggable authentication, in which plugins are
invoked to authenticate client connections. Authentication
plugins enable the use of authentication methods other than
the built-in method of passwords stored in the
mysql.user
system table. For example,
plugins can be written to access external authentication
methods. Also, authentication plugins can support the proxy
user capability, such that the connecting user is a proxy for
another user and is treated, for purposes of access control,
as having the privileges of a different user. For more
information, see Section 6.2.17, “Pluggable Authentication”,
and Section 6.2.18, “Proxy Users”.
An authentication plugin can be written for the server side or the client side. Server-side plugins use the same plugin API that is used for the other server plugin types such as full-text parser or audit plugins (although with a different type-specific descriptor). Client-side plugins use the client plugin API.
Several header files contain information relevant to authentication plugins:
plugin.h
: Defines theMYSQL_AUTHENTICATION_PLUGIN
server plugin type.client_plugin.h
: Defines the API for client plugins. This includes the client plugin descriptor and function prototypes for client plugin C API calls (see Section 28.7.17, “C API Client Plugin Functions”).plugin_auth.h
: Defines the part of the server plugin API specific to authentication plugins. This includes the type-specific descriptor for server-side authentication plugins and theMYSQL_SERVER_AUTH_INFO
structure.plugin_auth_common.h
: Contains common elements of client and server authentication plugins. This includes return value definitions and theMYSQL_PLUGIN_VIO
structure.
To write an authentication plugin, include the following header files in the plugin source file. Other MySQL or general header files might also be needed, depending on the plugin capabilities and requirements.
For a source file that implements a server authentication plugin, include this file:
#include <mysql/plugin_auth.h>
For a source file that implements a client authentication plugin, or both client and server plugins, include these files:
#include <mysql/plugin_auth.h> #include <mysql/client_plugin.h> #include <mysql.h>
plugin_auth.h
includes
plugin.h
and
plugin_auth_common.h
, so you need not
include the latter files explicitly.
This section describes how to write a pair of simple server and client authentication plugins that work together.
These plugins accept any non-empty password and the password is sent as cleartext. This is insecure, so the plugins should not be used in production environments.
The server-side and client-side plugins developed here both
are named auth_simple
. As described in
Section 29.2.4.2, “Plugin Data Structures”, the plugin library
file must have the same base name as the client plugin, so the
source file name is auth_simple.c
and
produces a library named auth_simple.so
(assuming that your system uses .so
as
the suffix for library files).
In MySQL source distributions, authentication plugin source is
located in the plugin/auth
directory and
can be examined as a guide to writing other authentication
plugins. Also, to see how the built-in authentication plugins
are implemented, see sql/sql_acl.cc
for
plugins that are built in to the MySQL server and
sql-common/client.c
for plugins that are
built in to the libmysqlclient
client
library. (For the built-in client plugins, note that the
auth_plugin_t
structures used there differ
from the structures used with the usual client plugin
declaration macros. In particular, the first two members are
provided explicitly, not by declaration macros.)
29.2.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 29.2.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, alwaysMYSQL_AUTHENTICATION_INTERFACE_VERSION
client_auth_plugin
: The client plugin nameauthenticate_user
: A pointer to the main plugin function that communicates with the clientgenerate_authentication_string
: A pointer to a plugin function that generates a password digest from an authentication stringvalidate_authentication_string
: A pointer to a plugin function that validates a password digestset_salt
: A pointer to a plugin function that converts a scrambled password to binary formauthentication_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 theUSER()
function value.user_name_length
: The length ofuser_name
in bytes.auth_string
: The value of theauthentication_string
column of the row in themysql.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:
When
my_user
connects from the local host, the server invokesmy_plugin
and passes'my_auth_string'
to it as theauth_string
value.auth_string_length
: The length ofauth_string
in bytes.authenticated_as
: The server sets this to the user name (the value ofuser_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 29.2.4.9.4, “Implementing Proxy User Support in Authentication Plugins”). The value is represented as a string at mostMYSQL_USER_NAME_LENGTH
bytes long, plus a terminating null. The value becomes theCURRENT_USER()
function value.external_user
: The server sets this to the empty string (null terminated). Its value becomes theexternal_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 ofAuthentication fails. Password used: %s
. The value ofpassword_used
determines how%s
is handled, as shown in the following table.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 ofhost_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
Section 6.2.18, “Proxy Users”, and
Section 29.2.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 is used only by the
mysql_native_password
plugin (see
Section 6.4.1.1, “Native Pluggable Authentication”). 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 globalCREATE USER
privilege or theUPDATE
privilege for themysql
database.AUTH_FLAG_USES_INTERNAL_STORAGE
: Whether the plugin uses internal storage (in theauthentication_string
column ofmysql.user
rows). If this flag is not set, attempts to set the password fail and the server produces a warning.
Declare the client-side plugin descriptor with the
mysql_declare_client_plugin()
and
mysql_end_client_plugin
macros (see
Section 29.2.4.2.3, “Client Plugin Descriptors”). For the
auth_simple
plugin, the descriptor looks
like this:
mysql_declare_client_plugin(AUTHENTICATION)
"auth_simple", /* plugin name */
"Author Name", /* author */
"Any-password authentication plugin", /* description */
{1,0,0}, /* version = 1.0.0 */
"GPL", /* license type */
NULL, /* for internal use */
NULL, /* no init function */
NULL, /* no deinit function */
NULL, /* no option-handling function */
auth_simple_client /* main function */
mysql_end_client_plugin;
The descriptor members from the plugin name through the option-handling function are common to all client plugin types. (For descriptions, see Section 29.2.4.2.3, “Client Plugin Descriptors”.) Following the common members, the descriptor has an additional member specific to authentication plugins. This is the “main” function, which handles communication with the server. The function takes two arguments representing an I/O structure and a connection handler. For our simple any-password plugin, the main function does nothing but write to the server the password provided by the user:
static int auth_simple_client (MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
{
int res;
/* send password as null-terminated string as cleartext */
res= vio->write_packet(vio, (const unsigned char *) mysql->passwd,
strlen(mysql->passwd) + 1);
return res ? CR_ERROR : CR_OK;
}
The main function should return one of the error codes shown in the following table.
CR_OK_HANDSHAKE_COMPLETE
indicates that
the client has done its part successfully and has read the
last packet. A client plugin may return
CR_OK_HANDSHAKE_COMPLETE
if the number of
round trips in the authentication protocol is not known in
advance and the plugin must read another packet to determine
whether authentication is finished.
To compile and install a plugin library file, use the
instructions in
Section 29.2.4.3, “Compiling and Installing Plugin Libraries”. To make the
library file available for use, install it in the plugin
directory (the directory named by the
plugin_dir
system
variable).
Register the server-side plugin with the server. For
example, to load the plugin at server startup, use a
--plugin-load=auth_simple.so
option (adjust the .so
suffix for your
platform as necessary).
Create a user for whom the server will use the
auth_simple
plugin for authentication:
Use a client program to connect to the server as user
x
. The server-side
auth_simple
plugin communicates with the
client program that it should use the client-side
auth_simple
plugin, and the latter sends
the password to the server. The server plugin should reject
connections that send an empty password and accept
connections that send a nonempty password. Invoke the client
program each way to verify this:
shell> mysql --user=x --skip-password
ERROR 1045 (28000): Access denied for user 'x'@'localhost' (using password: NO)
shell> mysql --user=x --password
Enter password: abc
mysql>
Because the server plugin accepts any nonempty password, it
should be considered insecure. After testing the plugin to
verify that it works, restart the server without the
--plugin-load
option so as
not to indavertently leave the server running with an
insecure authentication plugin loaded. Also, drop the user
with DROP USER
'x'@'localhost'
.
For additional information about loading and using authentication plugins, see Section 5.6.1, “Installing and Uninstalling Plugins”, and Section 6.2.17, “Pluggable Authentication”.
If you are writing a client program that supports the use of
authentication plugins, normally such a program causes a
plugin to be loaded by calling
mysql_options()
to set the
MYSQL_DEFAULT_AUTH
and
MYSQL_PLUGIN_DIR
options:
char *plugin_dir = "path_to_plugin_dir";
char *default_auth = "plugin_name";
/* ... process command-line options ... */
mysql_options(&mysql, MYSQL_PLUGIN_DIR, plugin_dir);
mysql_options(&mysql, MYSQL_DEFAULT_AUTH, default_auth);
Typically, the program will also accept
--plugin-dir
and
--default-auth
options that enable users to
override the default values.
Should a client program require lower-level plugin
management, the client library contains functions that take
an st_mysql_client_plugin
argument. See
Section 28.7.17, “C API Client Plugin Functions”.
One of the capabilities that pluggable authentication makes possible is proxy users (see Section 6.2.18, “Proxy Users”). For a server-side authentication plugin to participate in proxy user support, these conditions must be satisfied:
When a connecting client should be treated as a proxy user, the plugin must return a different name in the
authenticated_as
member of theMYSQL_SERVER_AUTH_INFO
structure, to indicate the proxied user name. It may also optionally set theexternal_user
member, to set the value of theexternal_user
system variable.Proxy user accounts must be set up to be authenticated by the plugin. Use the
CREATE USER
orGRANT
statement to associate accounts with plugins.Proxy user accounts must have the
PROXY
privilege for the proxied accounts. Use theGRANT
statement to grant this privilege.
In other words, the only aspect of proxy user support
required of the plugin is that it set
authenticated_as
to the proxied user
name. The rest is optional (setting
external_user
) or done by the DBA using
SQL statements.
How does an authentication plugin determine which proxied
user to return when the proxy user connects? That depends on
the plugin. Typically, the plugin maps clients to proxied
users based on the authentication string passed to it by the
server. This string comes from the AS
part of the IDENTIFIED WITH
clause of the
CREATE USER
statement that
specifies use of the plugin for authentication.
The plugin developer determines the syntax rules for the authentication string and implements the plugin according to those rules. Suppose that a plugin takes a comma-separated list of pairs that map external users to MySQL users. For example:
When the server invokes a plugin to authenticate a client, it passes the appropriate authentication string to the plugin. The plugin is responsible to:
Parse the string into its components to determine the mapping to use
Compare the client user name to the mapping
Return the proper MySQL user name
For example, if extuser2
connects from an
example.com
host, the server passes
'extuser1=mysqlusera,
extuser2=mysqluserb'
to the plugin, and the plugin
should copy mysqluserb
into
authenticated_as
, with a terminating null
byte. If extuser2
connects from an
example.org
host, the server passes
'extuser1=mysqluserc,
extuser2=mysqluserd'
, and the plugin should copy
mysqluserd
instead.
If there is no match in the mapping, the action depends on
the plugin. If a match is required, the plugin likely will
return an error. Or the plugin might simply return the
client name; in this case, it should not change
authenticated_as
, and the server will not
treat the client as a proxy.
The following example demonstrates how to handle proxy users
using a plugin named auth_simple_proxy
.
Like the auth_simple
plugin described
earlier, auth_simple_proxy
accepts any
nonempty password as valid (and thus should not be used in
production environments). In addition, it examines the
auth_string
authentication string member
and uses these very simple rules for interpreting it:
If the string is empty, the plugin returns the user name as given and no proxying occurs. That is, the plugin leaves the value of
authenticated_as
unchanged.If the string is nonempty, the plugin treats it as the name of the proxied user and copies it to
authenticated_as
so that proxying occurs.
For testing, set up one account that is not proxied
according to the preceding rules, and one that is. This
means that one account has no AS
clause,
and one includes an AS
clause that names
the proxied user:
- IDENTIFIED WITH auth_simple_proxy;
In addition, create an account for the proxied user and
grant plugin_user2
the
PROXY
privilege for it:
- IDENTIFIED BY 'proxied_user_pass';
- GRANT PROXY
Before the server invokes an authentication plugin, it sets
authenticated_as
to the client user name.
To indicate that the user is a proxy, the plugin should set
authenticated_as
to the proxied user
name. For auth_simple_proxy
, this means
that it must examine the auth_string
value, and, if the value is nonempty, copy it to the
authenticated_as
member to return it as
the name of the proxied user. In addition, when proxying
occurs, the plugin sets the external_user
member to the client user name; this becomes the value of
the external_user
system
variable.
static int auth_simple_proxy_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;
/* if authentication string is nonempty, use as proxied user name */
/* and use client name as external_user value */
if (info->auth_string_length > 0)
{
strcpy (info->authenticated_as, info->auth_string);
strcpy (info->external_user, info->user_name);
}
return CR_OK;
}
After a successful connection, the
USER()
function should
indicate the connecting client user and host name, and
CURRENT_USER()
should
indicate the account whose privileges apply during the
session. The latter value should be the connecting user
account if no proxying occurs or the proxied account if
proxying does occur.
Compile and install the plugin, then test it. First, connect
as plugin_user1
:
shell> mysql --user=plugin_user1 --password
Enter password: x
In this case, there should be no proxying:
Then connect as plugin_user2
:
shell> mysql --user=plugin_user2 --password
Enter password: x
In this case, plugin_user2
should be
proxied to proxied_user
:
- *************************** 1. row ***************************
- @@proxy_user: 'plugin_user2'@'localhost'
- @@external_user: 'plugin_user2'@'localhost'
Document created the 26/06/2006, last modified the 26/10/2018
Source of the printed document:https://www.gaudry.be/en/mysql-rf-writing-authentication-plugins.html
The infobrol is a personal site whose content is my sole responsibility. The text is available under CreativeCommons license (BY-NC-SA). More info on the terms of use and the author.
References
These references and links indicate documents consulted during the writing of this page, or which may provide additional information, but the authors of these sources can not be held responsible for the content of this page.
The author This site is solely responsible for the way in which the various concepts, and the freedoms that are taken with the reference works, are presented here. Remember that you must cross multiple source information to reduce the risk of errors.