summaryrefslogtreecommitdiffstats
path: root/ipalib/x509.py
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2011-06-08 10:54:41 -0400
committerRob Crittenden <rcritten@redhat.com>2011-06-21 19:09:50 -0400
commitdd69c7dbe68e8f8674994a54ea913f2dd2e52c32 (patch)
tree5fdc303354eb26a1d2cd206c81babdc73e8d51b9 /ipalib/x509.py
parent3a36eced53e540fe8f2b23eadf7dffda080324de (diff)
downloadfreeipa-dd69c7dbe68e8f8674994a54ea913f2dd2e52c32.tar.gz
freeipa-dd69c7dbe68e8f8674994a54ea913f2dd2e52c32.tar.xz
freeipa-dd69c7dbe68e8f8674994a54ea913f2dd2e52c32.zip
Make data type of certificates more obvious/predictable internally.
For the most part certificates will be treated as being in DER format. When we load a certificate we will generally accept it in any format but will convert it to DER before proceeding in normalize_certificate(). This also re-arranges a bit of code to pull some certificate-specific functions out of ipalib/plugins/service.py into ipalib/x509.py. This also tries to use variable names to indicate what format the certificate is in at any given point: dercert: DER cert: PEM nsscert: a python-nss Certificate object rawcert: unknown format ticket 32
Diffstat (limited to 'ipalib/x509.py')
-rw-r--r--ipalib/x509.py116
1 files changed, 110 insertions, 6 deletions
diff --git a/ipalib/x509.py b/ipalib/x509.py
index fc6f9c121..77d6aabf4 100644
--- a/ipalib/x509.py
+++ b/ipalib/x509.py
@@ -17,12 +17,30 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# Certificates should be stored internally DER-encoded. We can be passed
+# a certificate several ways: read if from LDAP, read it from a 3rd party
+# app (dogtag, candlepin, etc) or as user input. The normalize_certificate()
+# function will convert an incoming certificate to DER-encoding.
+
+# Conventions
+#
+# Where possible the following naming conventions are used:
+#
+# cert: the certificate is a PEM-encoded certificate
+# dercert: the certificate is DER-encoded
+# nsscert: the certificate is an NSS Certificate object
+# rawcert: the cert is in an unknown format
+
import os
import sys
import base64
import nss.nss as nss
+from nss.error import NSPRError
from ipapython import ipautil
from ipalib import api
+from ipalib import _
+from ipalib import util
+from ipalib import errors
PEM = 0
DER = 1
@@ -66,17 +84,103 @@ def get_subject(certificate, datatype=PEM):
Load an X509.3 certificate and get the subject.
"""
- cert = load_certificate(certificate, datatype)
- return cert.subject
+ nsscert = load_certificate(certificate, datatype)
+ return nsscert.subject
def get_serial_number(certificate, datatype=PEM):
"""
Return the decimal value of the serial number.
"""
- cert = load_certificate(certificate, datatype)
- return cert.serial_number
+ nsscert = load_certificate(certificate, datatype)
+ return nsscert.serial_number
+
+def make_pem(data):
+ """
+ Convert a raw base64-encoded blob into something that looks like a PE
+ file with lines split to 64 characters and proper headers.
+ """
+ pemcert = '\n'.join([data[x:x+64] for x in range(0, len(data), 64)])
+ return '-----BEGIN CERTIFICATE-----\n' + \
+ pemcert + \
+ '\n-----END CERTIFICATE-----'
+
+def normalize_certificate(rawcert):
+ """
+ Incoming certificates should be DER-encoded. If not it is converted to
+ DER-format.
+
+ Note that this can't be a normalizer on a Param because only unicode
+ variables are normalized.
+ """
+ if not rawcert:
+ return None
+
+ rawcert = strip_header(rawcert)
+
+ if util.isvalid_base64(rawcert):
+ try:
+ dercert = base64.b64decode(rawcert)
+ except Exception, e:
+ raise errors.Base64DecodeError(reason=str(e))
+ else:
+ dercert = rawcert
+
+ # 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:
+ serial = unicode(get_serial_number(dercert, 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 dercert
+
+def write_certificate(rawcert, filename):
+ """
+ Write the certificate to a file in PEM format.
+
+ The cert value can be either DER or PEM-encoded, it will be normalized
+ to DER regardless, then back out to PEM.
+ """
+ dercert = normalize_certificate(rawcert)
+
+ try:
+ fp = open(filename, 'w')
+ fp.write(make_pem(base64.b64encode(dercert)))
+ fp.close()
+ except (IOError, OSError), e:
+ raise errors.FileError(reason=str(e))
+
+def verify_cert_subject(ldap, hostname, dercert):
+ """
+ Verify that the certificate issuer we're adding matches the issuer
+ base of our installation.
+
+ This assumes the certificate has already been normalized.
+
+ This raises an exception on errors and returns nothing otherwise.
+ """
+ nsscert = load_certificate(dercert, datatype=DER)
+ subject = str(nsscert.subject)
+ issuer = str(nsscert.issuer)
+
+ # Handle both supported forms of issuer, from selfsign and dogtag.
+ if ((issuer != 'CN=%s Certificate Authority' % api.env.realm) and
+ (issuer != 'CN=Certificate Authority,O=%s' % api.env.realm)):
+ raise errors.CertificateOperationError(error=_('Issuer "%(issuer)s" does not match the expected issuer') % \
+ {'issuer' : issuer})
if __name__ == '__main__':
+ # this can be run with:
+ # python ipalib/x509.py < /etc/ipa/ca.crt
+
+ from ipalib import api
+ api.bootstrap()
+ api.finalize()
nss.nss_init_nodb()
@@ -85,6 +189,6 @@ if __name__ == '__main__':
certlines = sys.stdin.readlines()
cert = ''.join(certlines)
- cert = load_certificate(cert)
+ nsscert = load_certificate(cert)
- print cert
+ print nsscert