summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/errors.py18
-rw-r--r--ipalib/plugins/service.py50
-rw-r--r--ipalib/util.py22
3 files changed, 78 insertions, 12 deletions
diff --git a/ipalib/errors.py b/ipalib/errors.py
index 0d1304e0..42d43ce6 100644
--- a/ipalib/errors.py
+++ b/ipalib/errors.py
@@ -1204,7 +1204,7 @@ class CertificateError(ExecutionError):
errno = 4300
-class CertificateOperationError(ExecutionError):
+class CertificateOperationError(CertificateError):
"""
**4301** Raised when a certificate operation cannot be completed
@@ -1220,6 +1220,22 @@ class CertificateOperationError(ExecutionError):
errno = 4301
format = _('Certificate operation cannot be completed: %(error)s')
+class CertificateFormatError(CertificateError):
+ """
+ **4302** Raised when a certificate is badly formatted
+
+ For example:
+
+ >>> raise CertificateFormatError(error=u'improperly formated DER-encoded certificate')
+ Traceback (most recent call last):
+ ...
+ CertificateFormatError: improperly formated DER-encoded certificate
+
+ """
+
+ errno = 4302
+ format = _('Certificate format error: %(error)s')
+
class MutuallyExclusiveError(ExecutionError):
"""
diff --git a/ipalib/plugins/service.py b/ipalib/plugins/service.py
index 50e8d54f..d226f95a 100644
--- a/ipalib/plugins/service.py
+++ b/ipalib/plugins/service.py
@@ -75,6 +75,7 @@ from ipalib import Str, Flag, Bytes
from ipalib.plugins.baseldap import *
from ipalib import x509
from ipalib import _, ngettext
+from ipalib import util
from nss.error import NSPRError
@@ -130,10 +131,41 @@ def validate_certificate(ugettext, cert):
"""
For now just verify that it is properly base64-encoded.
"""
+ if util.isvalid_base64(cert):
+ try:
+ base64.b64decode(cert)
+ except Exception, e:
+ raise errors.Base64DecodeError(reason=str(e))
+ else:
+ # We'll assume this is DER data
+ pass
+
+def normalize_certificate(cert):
+ """
+ Incoming certificates should be DER-encoded.
+
+ Note that this can't be a normalizer on the Param because only unicode
+ variables are normalized.
+ """
+ if util.isvalid_base64(cert):
+ try:
+ cert = base64.b64decode(cert)
+ except Exception, e:
+ raise errors.Base64DecodeError(reason=str(e))
+
+ # At this point we should have a certificate, either because the data
+ # was base64-encoded and now its not or it came in as DER format.
+ # Let's decode it and see. Fetching the serial number will pass the
+ # certificate through the NSS DER parser.
try:
- base64.b64decode(cert)
- except Exception, e:
- raise errors.Base64DecodeError(reason=str(e))
+ serial = unicode(x509.get_serial_number(cert, x509.DER))
+ except NSPRError, nsprerr:
+ if nsprerr.errno == -8183: # SEC_ERROR_BAD_DER
+ raise errors.CertificateFormatError(error='improperly formatted DER-encoded certificate')
+ else:
+ raise errors.CertificateFormatError(error=str(nsprerr))
+
+ return cert
class service(LDAPObject):
@@ -196,13 +228,9 @@ class service_add(LDAPCreate):
except errors.NotFound:
raise errors.NotFound(reason="The host '%s' does not exist to add a service to." % hostname)
- cert = entry_attrs.get('usercertificate')
+ cert = options.get('usercertificate')
if cert:
- cert = cert[0]
- # FIXME: should be in a normalizer: need to fix normalizers
- # to work on non-unicode data
- entry_attrs['usercertificate'] = base64.b64decode(cert)
- # FIXME: shouldn't we request signing at this point?
+ entry_attrs['usercertificate'] = normalize_certificate(cert)
if not options.get('force', False):
# We know the host exists if we've gotten this far but we
@@ -273,6 +301,7 @@ class service_mod(LDAPUpdate):
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
if 'usercertificate' in options:
cert = options.get('usercertificate')
+ cert = normalize_certificate(cert)
if cert:
(dn, entry_attrs_old) = ldap.get_entry(dn, ['usercertificate'])
if 'usercertificate' in entry_attrs_old:
@@ -281,8 +310,7 @@ class service_mod(LDAPUpdate):
x509.get_serial_number(entry_attrs_old['usercertificate'][0], x509.DER)
)
raise errors.GenericError(format=fmt)
- # FIXME: should be in normalizer; see service_add
- entry_attrs['usercertificate'] = base64.b64decode(cert)
+ entry_attrs['usercertificate'] = cert
else:
entry_attrs['usercertificate'] = None
return dn
diff --git a/ipalib/util.py b/ipalib/util.py
index 6bd1da54..1803e65a 100644
--- a/ipalib/util.py
+++ b/ipalib/util.py
@@ -26,6 +26,7 @@ import imp
import logging
import time
import socket
+import re
from types import NoneType
from ipalib import errors
@@ -148,3 +149,24 @@ def validate_host_dns(log, fqdn):
log.debug(
'IPA: found %d records for %s' % (len(rs), fqdn)
)
+
+def isvalid_base64(data):
+ """
+ Validate the incoming data as valid base64 data or not.
+
+ The character set must only include of a-z, A-Z, 0-9, + or / and
+ be padded with = to be a length divisible by 4 (so only 0-2 =s are
+ allowed). Its length must be divisible by 4. White space is
+ not significant so it is removed.
+
+ This doesn't guarantee we have a base64-encoded value, just that it
+ fits the base64 requirements.
+ """
+
+ data = ''.join(data.split())
+
+ if len(data) % 4 > 0 or \
+ re.match('^[a-zA-Z0-9\+\/]+\={0,2}$', data) is None:
+ return False
+ else:
+ return True