#
#
-# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
+# Copyright (C) 2006, 2007, 2010, 2011, 2012 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
import re
import datetime
import calendar
+import errno
+import logging
from ganeti import errors
from ganeti import constants
+from ganeti import pathutils
from ganeti.utils import text as utils_text
from ganeti.utils import io as utils_io
# Depending on the pyOpenSSL version, this can just return (None, None)
(not_before, not_after) = GetX509CertValidity(cert)
+ now = time.time() + constants.NODE_MAX_CLOCK_SKEW
+
return _VerifyCertificateInner(cert.has_expired(), not_before, not_after,
- time.time(), warn_days, error_days)
+ now, warn_days, error_days)
def SignX509Certificate(cert, key, salt):
"""
(salt, signature) = _ExtractX509CertificateSignature(cert_pem)
- # Load certificate
- cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem)
-
- # Dump again to ensure it's in a sane format
- sane_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
+ # Load and dump certificate to ensure it's in a sane format
+ (cert, sane_pem) = ExtractX509Certificate(cert_pem)
if not utils_hash.VerifySha1Hmac(key, sane_pem, signature, salt=salt):
raise errors.GenericError("X509 certificate signature is invalid")
@param common_name: commonName value
@type validity: int
@param validity: Validity for certificate in seconds
+ @return: a tuple of strings containing the PEM-encoded private key and
+ certificate
"""
# Create private and public key
@param common_name: commonName value
@type validity: int
@param validity: validity of certificate in number of days
+ @return: a tuple of strings containing the PEM-encoded private key and
+ certificate
"""
# TODO: Investigate using the cluster name instead of X505_CERT_CN for
validity * 24 * 60 * 60)
utils_io.WriteFile(filename, mode=0400, data=key_pem + cert_pem)
+ return (key_pem, cert_pem)
+
+
+def ExtractX509Certificate(pem):
+ """Extracts the certificate from a PEM-formatted string.
+
+ @type pem: string
+ @rtype: tuple; (OpenSSL.X509 object, string)
+ @return: Certificate object and PEM-formatted certificate
+
+ """
+ cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, pem)
+
+ return (cert,
+ OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert))
+
+
+def PrepareX509CertKeyCheck(cert, key):
+ """Get function for verifying certificate with a certain private key.
+
+ @type key: OpenSSL.crypto.PKey
+ @param key: Private key object
+ @type cert: OpenSSL.crypto.X509
+ @param cert: X509 certificate object
+ @rtype: callable
+ @return: Callable doing the actual check; will raise C{OpenSSL.SSL.Error} if
+ certificate is not signed by given private key
+
+ """
+ ctx = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD)
+ ctx.use_privatekey(key)
+ ctx.use_certificate(cert)
+
+ return ctx.check_privatekey
+
+
+def CheckNodeCertificate(cert, _noded_cert_file=pathutils.NODED_CERT_FILE):
+ """Checks the local node daemon certificate against given certificate.
+
+ Both certificates must be signed with the same key (as stored in the local
+ L{pathutils.NODED_CERT_FILE} file). No error is raised if no local
+ certificate can be found.
+
+ @type cert: OpenSSL.crypto.X509
+ @param cert: X509 certificate object
+ @raise errors.X509CertError: When an error related to X509 occurred
+ @raise errors.GenericError: When the verification failed
+
+ """
+ try:
+ noded_pem = utils_io.ReadFile(_noded_cert_file)
+ except EnvironmentError, err:
+ if err.errno != errno.ENOENT:
+ raise
+
+ logging.debug("Node certificate file '%s' was not found", _noded_cert_file)
+ return
+
+ try:
+ noded_cert = \
+ OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, noded_pem)
+ except Exception, err:
+ raise errors.X509CertError(_noded_cert_file,
+ "Unable to load certificate: %s" % err)
+
+ try:
+ noded_key = \
+ OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, noded_pem)
+ except Exception, err:
+ raise errors.X509CertError(_noded_cert_file,
+ "Unable to load private key: %s" % err)
+
+ # Check consistency of server.pem file
+ check_fn = PrepareX509CertKeyCheck(noded_cert, noded_key)
+ try:
+ check_fn()
+ except OpenSSL.SSL.Error:
+ # This should never happen as it would mean the certificate in server.pem
+ # is out of sync with the private key stored in the same file
+ raise errors.X509CertError(_noded_cert_file,
+ "Certificate does not match with private key")
+
+ # Check with supplied certificate with local key
+ check_fn = PrepareX509CertKeyCheck(cert, noded_key)
+ try:
+ check_fn()
+ except OpenSSL.SSL.Error:
+ raise errors.GenericError("Given cluster certificate does not match"
+ " local key")