Implementing Proxy User Support in Authentication Plugins
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 MariaDB 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 MariaDB 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
: