One of the capabilities that pluggable authentication makes possible is proxy users (see 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:
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
:
$> mysql --user=plugin_user1 --password
Enter 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
:
$> mysql --user=plugin_user2 --password
Enter 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'