Spec-Zone .ru
спецификации, руководства, описания, API
|
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
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.3.7, "Pluggable Authentication", and Section 6.3.8, "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 the MYSQL_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 22.8.14, "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 the MYSQL_SERVER_AUTH_INFO
structure.
plugin_auth_common.h
: Contains common elements of
client and server authentication plugins. This includes return value definitions and the MYSQL_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.
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>
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 in clear text. 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 23.2.4.2, "Plugin Data Structures", the
plugin library file must have the same basename 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.)
Declare the server-side plugin with the usual general descriptor format that is used for all server plugin
types (see Section 23.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);};
The st_mysql_auth
structure has three members: The type-specific API version
number, the client plugin name, and a pointer to the main plugin function that communicates with the client.
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 */};
The main function takes two arguments representing an I/O structure and a MYSQL_SERVER_AUTH_INFO
structure. The structure definition is found in plugin_auth.h
. It 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 mysql.user
table row 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
23.2.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 error codes following CR_ERROR
are available from MySQL 5.6.5. The server
counts plugin errors in the Performance Schema host_cache
table, also available as of MySQL 5.6.5.
auth_simple_server_main()
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.3.8, "Proxy Users", and Section
23.2.4.9.4, "Implementing Proxy User Support in Authentication Plugins".
Declare the client-side plugin descriptor with the mysql_declare_client_plugin()
and mysql_end_client_plugin
macros (see Section
23.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 common to all client plugins are the plugin name through the option-handling function. Following the common members, an authentication plugin also has one extra member. 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 in clear text */ 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.
Error Code | Meaning |
---|---|
CR_OK |
Success |
CR_OK_HANDSHAKE_COMPLETE |
Success, client done |
CR_ERROR |
Error |
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 object file, see the instructions in Section
23.2.4.3, "Compiling and Installing Plugin Libraries". To use the library file, it must be installed 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 (change the library suffix as necessary for your system).
Create a user for whom the server will use the auth_simple
plugin for
authentication:
mysql>CREATE USER 'x'@'localhost'
->IDENTIFIED WITH auth_simple;
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=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.1.8.1, "Installing and Uninstalling Plugins", and Section 6.3.7, "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
22.8.14, "C API Client Plugin Functions".
One of the capabilities that pluggable authentication makes possible is proxy users (see Section 6.3.8, "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 the MYSQL_SERVER_AUTH_INFO
structure, to indicate the proxied user name.
It may also optionally set the external_user
member, to set the value
of the external_user
system variable.
Proxy user accounts must be set up to be authenticated by the plugin. Use the
CREATE USER
or GRANT
statement to associate accounts with plugins.
Proxy user accounts must have the PROXY
privilege for the proxied accounts. Use the GRANT
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:
CREATE USER ''@'%.example.com' IDENTIFIED WITH my_plugin AS 'extuser1=mysqlusera, extuser2=mysqluserb'CREATE USER ''@'%.example.org' IDENTIFIED WITH my_plugin AS 'extuser1=mysqluserc, extuser2=mysqluserd'
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:
CREATE USER 'plugin_user1'@'localhost' IDENTIFIED WITH auth_simple_proxy;CREATE USER 'plugin_user2'@'localhost' IDENTIFIED WITH auth_simple_proxy AS 'proxied_user';
In addition, create an account for the proxied user and grant plugin_user2
the
PROXY
privilege for it:
CREATE USER 'proxied_user'@'localhost' IDENTIFIED BY 'proxied_user_pass';GRANT PROXY ON 'proxied_user'@'localhost' TO 'plugin_user2'@'localhost';
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=x
In this case, there should be no proxying:
mysql> SELECT USER(), CURRENT_USER(),
@@proxy_user, @@external_user\G
*************************** 1. row *************************** USER(): plugin_user1@localhost CURRENT_USER(): plugin_user1@localhost @@proxy_user: NULL@@external_user: NULL
Then connect as plugin_user2
:
shell> mysql --user=plugin_user2
--password=x
In this case, plugin_user2
should be proxied to proxied_user
:
mysql> SELECT USER(), CURRENT_USER(),
@@proxy_user, @@external_user\G
*************************** 1. row *************************** USER(): plugin_user2@localhost CURRENT_USER(): proxied_user@localhost @@proxy_user: 'plugin_user2'@'localhost'@@external_user: 'plugin_user2'@'localhost'