summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Prince <dprince@redhat.com>2013-01-10 13:25:18 -0500
committerDan Prince <dprince@redhat.com>2013-02-05 09:20:57 -0500
commit8ec247bf61be0e487332d5d891246d2b7b606989 (patch)
treeab9ac2196007c3d87f2e0a6b1669a18c1c908314
parentb3d667ab3bac874eda68c0ea0280f9e506f854aa (diff)
downloadkeystone-8ec247bf61be0e487332d5d891246d2b7b606989.tar.gz
keystone-8ec247bf61be0e487332d5d891246d2b7b606989.tar.xz
keystone-8ec247bf61be0e487332d5d891246d2b7b606989.zip
Add size validations to token controller.
Updates token controller so that it explicitly checks the max size of userId, username, tenantId, tenantname, token, and password before continuing with a request. Previously, when used with the SQL keystone backend an unauthenticated user could send in *really* large requests which would ultimately log large SQL exceptions and could thus fill up keystone logs on the disk. Change-Id: Ie7e3a958829f99f080e66582bdf558cded70248c
-rw-r--r--keystone/config.py3
-rw-r--r--keystone/exception.py13
-rw-r--r--keystone/token/controllers.py47
-rw-r--r--tests/test_auth.py47
4 files changed, 96 insertions, 14 deletions
diff --git a/keystone/config.py b/keystone/config.py
index e7f31394..40af2fd6 100644
--- a/keystone/config.py
+++ b/keystone/config.py
@@ -141,6 +141,9 @@ register_str('policy_file', default='policy.json')
register_str('policy_default_rule', default=None)
#default max request size is 112k
register_int('max_request_body_size', default=114688)
+register_int('max_param_size', default=64)
+# we allow tokens to be a bit larger to accomidate PKI
+register_int('max_token_size', default=8192)
# identity
register_str('default_domain_id', group='identity', default='default')
diff --git a/keystone/exception.py b/keystone/exception.py
index fe978061..5060dbfd 100644
--- a/keystone/exception.py
+++ b/keystone/exception.py
@@ -84,6 +84,19 @@ class StringLengthExceeded(ValidationError):
%(type)s(CHAR(%(length)d))."""
+class ValidationSizeError(Error):
+ """Request attribute %(attribute)s must be less than or equal to %(size)i.
+
+ The server could not comply with the request because the attribute
+ size is invalid (too large).
+
+ The client is assumed to be in error.
+
+ """
+ code = 400
+ title = 'Bad Request'
+
+
class SecurityError(Error):
"""Avoids exposing details of security failures, unless in debug mode."""
diff --git a/keystone/token/controllers.py b/keystone/token/controllers.py
index 1b51b9a1..62134028 100644
--- a/keystone/token/controllers.py
+++ b/keystone/token/controllers.py
@@ -5,12 +5,13 @@ from keystone.common import cms
from keystone.common import controller
from keystone.common import dependency
from keystone.common import logging
+from keystone.common import utils
from keystone import config
from keystone import exception
from keystone.openstack.common import timeutils
from keystone.token import core
-
+CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -22,13 +23,13 @@ class ExternalAuthNotApplicable(Exception):
@dependency.requires('catalog_api')
class Auth(controller.V2Controller):
def ca_cert(self, context, auth=None):
- ca_file = open(config.CONF.signing.ca_certs, 'r')
+ ca_file = open(CONF.signing.ca_certs, 'r')
data = ca_file.read()
ca_file.close()
return data
def signing_cert(self, context, auth=None):
- cert_file = open(config.CONF.signing.certfile, 'r')
+ cert_file = open(CONF.signing.certfile, 'r')
data = cert_file.read()
cert_file.close()
return data
@@ -134,17 +135,17 @@ class Auth(controller.V2Controller):
service_catalog = Auth.format_catalog(catalog_ref)
token_data['access']['serviceCatalog'] = service_catalog
- if config.CONF.signing.token_format == 'UUID':
+ if CONF.signing.token_format == 'UUID':
token_id = uuid.uuid4().hex
- elif config.CONF.signing.token_format == 'PKI':
+ elif CONF.signing.token_format == 'PKI':
token_id = cms.cms_sign_token(json.dumps(token_data),
- config.CONF.signing.certfile,
- config.CONF.signing.keyfile)
+ CONF.signing.certfile,
+ CONF.signing.keyfile)
else:
raise exception.UnexpectedError(
'Invalid value for token_format: %s.'
' Allowed values are PKI or UUID.' %
- config.CONF.signing.token_format)
+ CONF.signing.token_format)
try:
self.token_api.create_token(
context, token_id, dict(key=token_id,
@@ -180,6 +181,9 @@ class Auth(controller.V2Controller):
attribute="id", target="token")
old_token = auth['token']['id']
+ if len(old_token) > CONF.max_token_size:
+ raise exception.ValidationSizeError(attribute='token',
+ size=CONF.max_token_size)
try:
old_token_ref = self.token_api.get_token(context=context,
@@ -228,6 +232,10 @@ class Auth(controller.V2Controller):
attribute='password', target='passwordCredentials')
password = auth['passwordCredentials']['password']
+ max_pw_size = utils.MAX_PASSWORD_LENGTH
+ if password and len(password) > max_pw_size:
+ raise exception.ValidationSizeError(attribute='password',
+ size=max_pw_size)
if ("userId" not in auth['passwordCredentials'] and
"username" not in auth['passwordCredentials']):
@@ -236,7 +244,14 @@ class Auth(controller.V2Controller):
target='passwordCredentials')
user_id = auth['passwordCredentials'].get('userId', None)
+ if user_id and len(user_id) > CONF.max_param_size:
+ raise exception.ValidationSizeError(attribute='userId',
+ size=CONF.max_param_size)
+
username = auth['passwordCredentials'].get('username', '')
+ if len(username) > CONF.max_param_size:
+ raise exception.ValidationSizeError(attribute='username',
+ size=CONF.max_param_size)
if username:
try:
@@ -323,7 +338,15 @@ class Auth(controller.V2Controller):
Returns a valid tenant_id if it exists, or None if not specified.
"""
tenant_id = auth.get('tenantId', None)
+ if tenant_id and len(tenant_id) > CONF.max_param_size:
+ raise exception.ValidationSizeError(attribute='tenantId',
+ size=CONF.max_param_size)
+
tenant_name = auth.get('tenantName', None)
+ if tenant_name and len(tenant_name) > CONF.max_param_size:
+ raise exception.ValidationSizeError(attribute='tenantName',
+ size=CONF.max_param_size)
+
if tenant_name:
try:
tenant_ref = self.identity_api.get_project_by_name(
@@ -410,8 +433,8 @@ class Auth(controller.V2Controller):
if cms.is_ans1_token(token_id):
data = json.loads(cms.cms_verify(cms.token_to_cms(token_id),
- config.CONF.signing.certfile,
- config.CONF.signing.ca_certs))
+ CONF.signing.certfile,
+ CONF.signing.ca_certs))
data['access']['token']['user'] = data['access']['user']
data['access']['token']['metadata'] = data['access']['metadata']
if belongs_to:
@@ -482,8 +505,8 @@ class Auth(controller.V2Controller):
data = {'revoked': tokens}
json_data = json.dumps(data)
signed_text = cms.cms_sign_text(json_data,
- config.CONF.signing.certfile,
- config.CONF.signing.keyfile)
+ CONF.signing.certfile,
+ CONF.signing.keyfile)
return {'signed': signed_text}
diff --git a/tests/test_auth.py b/tests/test_auth.py
index 1ca248d0..e8583153 100644
--- a/tests/test_auth.py
+++ b/tests/test_auth.py
@@ -27,8 +27,8 @@ from keystone import token
CONF = config.CONF
-def _build_user_auth(token=None, username=None,
- password=None, tenant_name=None):
+def _build_user_auth(token=None, user_id=None, username=None,
+ password=None, tenant_id=None, tenant_name=None):
"""Build auth dictionary.
It will create an auth dictionary based on all the arguments
@@ -41,10 +41,14 @@ def _build_user_auth(token=None, username=None,
auth_json['passwordCredentials'] = {}
if username is not None:
auth_json['passwordCredentials']['username'] = username
+ if user_id is not None:
+ auth_json['passwordCredentials']['userId'] = user_id
if password is not None:
auth_json['passwordCredentials']['password'] = password
if tenant_name is not None:
auth_json['tenantName'] = tenant_name
+ if tenant_id is not None:
+ auth_json['tenantId'] = tenant_id
return auth_json
@@ -121,6 +125,45 @@ class AuthBadRequests(AuthTest):
self.assertRaises(exception.ValidationError, self.api.authenticate,
{}, {'auth': 'abcd'})
+ def test_authenticate_user_id_too_large(self):
+ """Verify sending large 'userId' raises the right exception."""
+ body_dict = _build_user_auth(user_id='0' * 65, username='FOO',
+ password='foo2')
+ self.assertRaises(exception.ValidationSizeError, self.api.authenticate,
+ {}, body_dict)
+
+ def test_authenticate_username_too_large(self):
+ """Verify sending large 'username' raises the right exception."""
+ body_dict = _build_user_auth(username='0' * 65, password='foo2')
+ self.assertRaises(exception.ValidationSizeError, self.api.authenticate,
+ {}, body_dict)
+
+ def test_authenticate_tenant_id_too_large(self):
+ """Verify sending large 'tenantId' raises the right exception."""
+ body_dict = _build_user_auth(username='FOO', password='foo2',
+ tenant_id='0' * 65)
+ self.assertRaises(exception.ValidationSizeError, self.api.authenticate,
+ {}, body_dict)
+
+ def test_authenticate_tenant_name_too_large(self):
+ """Verify sending large 'tenantName' raises the right exception."""
+ body_dict = _build_user_auth(username='FOO', password='foo2',
+ tenant_name='0' * 65)
+ self.assertRaises(exception.ValidationSizeError, self.api.authenticate,
+ {}, body_dict)
+
+ def test_authenticate_token_too_large(self):
+ """Verify sending large 'token' raises the right exception."""
+ body_dict = _build_user_auth(token={'id': '0' * 8193})
+ self.assertRaises(exception.ValidationSizeError, self.api.authenticate,
+ {}, body_dict)
+
+ def test_authenticate_password_too_large(self):
+ """Verify sending large 'password' raises the right exception."""
+ body_dict = _build_user_auth(username='FOO', password='0' * 8193)
+ self.assertRaises(exception.ValidationSizeError, self.api.authenticate,
+ {}, body_dict)
+
class AuthWithToken(AuthTest):
def setUp(self):