{"id":3871,"date":"2011-08-09T13:19:10","date_gmt":"2011-08-09T11:19:10","guid":{"rendered":"http:\/\/code.openark.org\/blog\/?p=3871"},"modified":"2011-08-09T13:19:10","modified_gmt":"2011-08-09T11:19:10","slug":"finding-current_user-for-any-user","status":"publish","type":"post","link":"https:\/\/code.openark.org\/blog\/mysql\/finding-current_user-for-any-user","title":{"rendered":"Finding CURRENT_USER for any user"},"content":{"rendered":"<p>A MySQL account is a user\/host combination. A MySQL connection is done by a user connecting from some host.<\/p>\n<p>However, the user\/host from which the connection is made are not the same as the user\/host as specified in the account. For example, the account may be created thus:<\/p>\n<blockquote>\n<pre>CREATE USER 'temp'@'10.0.0.%' IDENTIFIED BY '123456';<\/pre>\n<\/blockquote>\n<p>The host as specified in the above account is a wildcard host. A connection by the <strong>&#8216;temp&#8217;<\/strong> user from <strong>&#8216;10.0.0.3&#8217;<\/strong> can map into that account. It thus happens that the connected user is <strong>&#8216;temp&#8217;@&#8217;10.0.0.3&#8217;<\/strong>, yet the assigned account is <strong>&#8216;temp&#8217;@&#8217;10.0.0.%&#8217;<\/strong>.<\/p>\n<p>MySQL provides with the <strong>USER()<\/strong> and <strong>CURRENT_USER()<\/strong> which map to the connected user and the assigned account, respectively, and which lets the current session identify the relation between the two. Read more on this on the <a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.1\/en\/account-activity-auditing.html\">MySQL docs<\/a>.<\/p>\n<h4>The problem<\/h4>\n<p>And the trouble is: MySQL only provides this functionality for the <em>current session<\/em>. Surprisingly, given a user\/host combination, I cannot get MySQL to tell me which account matches those details.<\/p>\n<h4>The inconsistency<\/h4>\n<p>And I care because there is an inconsistency. Namely, when I do <strong>SHOW PROCESSLIST<\/strong> MySQL tells me the user &amp; host from which the connection is made. It does <em>not<\/em> tell me the account for which the process is assigned.<!--more--><\/p>\n<blockquote>\n<pre>root@mysql-5.1.51&gt; SHOW PROCESSLIST;\r\n+----+------+----------------+---------------+---------+------+-------+------------------+\r\n| Id | User | Host\u00a0\u00a0\u00a0\u00a0\u00a0      | db\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | Command | Time | State | Info\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |\r\n+----+------+----------------+---------------+---------+------+-------+------------------+\r\n| 16 | temp | 10.0.0.3:54142 | common_schema | Query\u00a0\u00a0 |\u00a0\u00a0\u00a0 0 | NULL\u00a0 | SELECT id, ...   |\r\n+----+------+----------------+---------------+---------+------+-------+------------------+<\/pre>\n<\/blockquote>\n<p>The absurdness is that a super user, the manager of a MySQL server, has the full listing of connections, yet is unable to map those connections to accounts.<\/p>\n<p>I got into this because of a suggestion by <a href=\"http:\/\/forge.mysql.com\/people\/person.php?id=340\">Matthew Montgomery<\/a> to include a <a href=\"http:\/\/forge.mysql.com\/tools\/tool.php?id=106\">tool<\/a> of his into <a href=\"http:\/\/code.openark.org\/forge\/common_schema\">common_schema<\/a>.The tool says &#8220;Kill all slow queries which are not executed by users with the SUPER privilege&#8221;.<\/p>\n<p>Great idea! But then, how do you identify such users?<\/p>\n<p>The tool attempts to find an exact match between <strong>INFORMATION_SCHEMA.PROCESSLIST<\/strong>&#8216;s user\/host and <strong>INFORMATION_SCHEMA.USER_PRIVILEGES<\/strong>&#8216;s user\/host. This will do well when the account is <strong>&#8216;root&#8217;@&#8217;localhost&#8217;<\/strong>, but less so when it is <strong>&#8216;maatkit&#8217;@&#8217;%.mydomain&#8217;<\/strong>.<\/p>\n<p>Unfortunately, many things fall under <strong>SUPER<\/strong>&#8216;s attention, and such accounts as monitoring, backup, management may require that privilege.<\/p>\n<h4>Account matching<\/h4>\n<p>Matching is achievable, but not completely trivial. If you&#8217;re not aware of this, you should note that <strong>&#8216;temp&#8217;@&#8217;10.0.0.3&#8217;<\/strong> can match any of the following:<\/p>\n<blockquote>\n<pre>+------+----------+\r\n| temp | 10.0.%\u00a0\u00a0 |\r\n| %\u00a0\u00a0\u00a0 | 10.0.0.3 |\r\n| temp | 10.0.0.3 |\r\n| temp | 10.0.0.% |\r\n+------+----------+<\/pre>\n<\/blockquote>\n<p>And the rule is we must match by most specific host first, then by most specific user. The order of matching should be this:<\/p>\n<blockquote>\n<pre>+------+----------+\r\n| temp | 10.0.0.3 |\r\n| %\u00a0\u00a0\u00a0 | 10.0.0.3 |\r\n| temp | 10.0.0.% |\r\n| temp | 10.0.%\u00a0\u00a0 |\r\n+------+----------+<\/pre>\n<\/blockquote>\n<p>The first row to match our connection&#8217;s user\/host is the matched account.<\/p>\n<h4>The good news<\/h4>\n<p>This means the problem is reduced to an ORDER BY and to regular expressions. Easy enough to do with SQL. We prefer hosts with no wildcard to those with; we prefer more subdomains, we prefer no wildcard for users.<\/p>\n<h4>The code<\/h4>\n<p>The following query assumes you have two session variables: <strong>@connection_user<\/strong> and <strong>@connection_host<\/strong>.<\/p>\n<blockquote>\n<pre>SELECT\r\n  user, host\r\nFROM\r\n  mysql.user\r\nWHERE\r\n  @connection_user RLIKE\r\n    CONCAT('^',\r\n      REPLACE(\r\n        user,\r\n        '%', '.*'),\r\n      '$')\r\n  AND SUBSTRING_INDEX(@connection_host, ':', 1) RLIKE\r\n    CONCAT('^',\r\n      REPLACE(\r\n      REPLACE(\r\n        host,\r\n        '.', '\\\\.'),\r\n        '%', '.*'),\r\n      '$')\r\nORDER BY\r\n  CHAR_LENGTH(host) - CHAR_LENGTH(REPLACE(host, '%', '')) ASC,\r\n  CHAR_LENGTH(host) - CHAR_LENGTH(REPLACE(host, '.', '')) DESC,\r\n  host ASC,\r\n  CHAR_LENGTH(user) - CHAR_LENGTH(REPLACE(user, '%', '')) ASC,\r\n  user ASC\r\nLIMIT 1\r\n;<\/pre>\n<\/blockquote>\n<p>There is still a slight fine-tuning to do for the above, but it should work for the majority of security setups.<\/p>\n<p>The above (in rewritten form) and derivative work will, of course, be part of the next <a href=\"..\/..\/forge\/common_schema\">common_schema<\/a> release, expected early September.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A MySQL account is a user\/host combination. A MySQL connection is done by a user connecting from some host. However, the user\/host from which the connection is made are not the same as the user\/host as specified in the account. For example, the account may be created thus: CREATE USER &#8216;temp&#8217;@&#8217;10.0.0.%&#8217; IDENTIFIED BY &#8216;123456&#8217;; The [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"enabled":false},"version":2}},"categories":[5],"tags":[67,16,21],"class_list":["post-3871","post","type-post","status-publish","format-standard","hentry","category-mysql","tag-common_schema","tag-security","tag-sql"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p2bZZp-10r","_links":{"self":[{"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/posts\/3871","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/comments?post=3871"}],"version-history":[{"count":24,"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/posts\/3871\/revisions"}],"predecessor-version":[{"id":3907,"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/posts\/3871\/revisions\/3907"}],"wp:attachment":[{"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/media?parent=3871"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/categories?post=3871"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/tags?post=3871"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}