Statistics
| Branch: | Tag: | Revision:

root / lib / utils / x509.py @ 0602cef3

History | View | Annotate | Download (12.3 kB)

1 c50645c0 Michael Hanselmann
#
2 c50645c0 Michael Hanselmann
#
3 c50645c0 Michael Hanselmann
4 f97a7ada Iustin Pop
# Copyright (C) 2006, 2007, 2010, 2011, 2012 Google Inc.
5 c50645c0 Michael Hanselmann
#
6 c50645c0 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 c50645c0 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 c50645c0 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 c50645c0 Michael Hanselmann
# (at your option) any later version.
10 c50645c0 Michael Hanselmann
#
11 c50645c0 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 c50645c0 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 c50645c0 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 c50645c0 Michael Hanselmann
# General Public License for more details.
15 c50645c0 Michael Hanselmann
#
16 c50645c0 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 c50645c0 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 c50645c0 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 c50645c0 Michael Hanselmann
# 02110-1301, USA.
20 c50645c0 Michael Hanselmann
21 c50645c0 Michael Hanselmann
"""Utility functions for X509.
22 c50645c0 Michael Hanselmann

23 c50645c0 Michael Hanselmann
"""
24 c50645c0 Michael Hanselmann
25 c50645c0 Michael Hanselmann
import time
26 c50645c0 Michael Hanselmann
import OpenSSL
27 c50645c0 Michael Hanselmann
import re
28 c50645c0 Michael Hanselmann
import datetime
29 c50645c0 Michael Hanselmann
import calendar
30 0602cef3 Michael Hanselmann
import errno
31 0602cef3 Michael Hanselmann
import logging
32 c50645c0 Michael Hanselmann
33 c50645c0 Michael Hanselmann
from ganeti import errors
34 c50645c0 Michael Hanselmann
from ganeti import constants
35 0602cef3 Michael Hanselmann
from ganeti import pathutils
36 c50645c0 Michael Hanselmann
37 c50645c0 Michael Hanselmann
from ganeti.utils import text as utils_text
38 c50645c0 Michael Hanselmann
from ganeti.utils import io as utils_io
39 c50645c0 Michael Hanselmann
from ganeti.utils import hash as utils_hash
40 c50645c0 Michael Hanselmann
41 c50645c0 Michael Hanselmann
42 c50645c0 Michael Hanselmann
HEX_CHAR_RE = r"[a-zA-Z0-9]"
43 c50645c0 Michael Hanselmann
VALID_X509_SIGNATURE_SALT = re.compile("^%s+$" % HEX_CHAR_RE, re.S)
44 c50645c0 Michael Hanselmann
X509_SIGNATURE = re.compile(r"^%s:\s*(?P<salt>%s+)/(?P<sign>%s+)$" %
45 c50645c0 Michael Hanselmann
                            (re.escape(constants.X509_CERT_SIGNATURE_HEADER),
46 c50645c0 Michael Hanselmann
                             HEX_CHAR_RE, HEX_CHAR_RE),
47 c50645c0 Michael Hanselmann
                            re.S | re.I)
48 c50645c0 Michael Hanselmann
49 c50645c0 Michael Hanselmann
# Certificate verification results
50 c50645c0 Michael Hanselmann
(CERT_WARNING,
51 c50645c0 Michael Hanselmann
 CERT_ERROR) = range(1, 3)
52 c50645c0 Michael Hanselmann
53 c50645c0 Michael Hanselmann
#: ASN1 time regexp
54 c50645c0 Michael Hanselmann
_ASN1_TIME_REGEX = re.compile(r"^(\d+)([-+]\d\d)(\d\d)$")
55 c50645c0 Michael Hanselmann
56 c50645c0 Michael Hanselmann
57 c50645c0 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
58 c50645c0 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
59 c50645c0 Michael Hanselmann

60 c50645c0 Michael Hanselmann
  @type value: string
61 c50645c0 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
62 c50645c0 Michael Hanselmann
  @return: Seconds since the Epoch (1970-01-01 00:00:00 UTC)
63 c50645c0 Michael Hanselmann

64 c50645c0 Michael Hanselmann
  """
65 c50645c0 Michael Hanselmann
  m = _ASN1_TIME_REGEX.match(value)
66 c50645c0 Michael Hanselmann
  if m:
67 c50645c0 Michael Hanselmann
    # We have an offset
68 c50645c0 Michael Hanselmann
    asn1time = m.group(1)
69 c50645c0 Michael Hanselmann
    hours = int(m.group(2))
70 c50645c0 Michael Hanselmann
    minutes = int(m.group(3))
71 c50645c0 Michael Hanselmann
    utcoffset = (60 * hours) + minutes
72 c50645c0 Michael Hanselmann
  else:
73 c50645c0 Michael Hanselmann
    if not value.endswith("Z"):
74 c50645c0 Michael Hanselmann
      raise ValueError("Missing timezone")
75 c50645c0 Michael Hanselmann
    asn1time = value[:-1]
76 c50645c0 Michael Hanselmann
    utcoffset = 0
77 c50645c0 Michael Hanselmann
78 c50645c0 Michael Hanselmann
  parsed = time.strptime(asn1time, "%Y%m%d%H%M%S")
79 c50645c0 Michael Hanselmann
80 c50645c0 Michael Hanselmann
  tt = datetime.datetime(*(parsed[:7])) - datetime.timedelta(minutes=utcoffset)
81 c50645c0 Michael Hanselmann
82 c50645c0 Michael Hanselmann
  return calendar.timegm(tt.utctimetuple())
83 c50645c0 Michael Hanselmann
84 c50645c0 Michael Hanselmann
85 c50645c0 Michael Hanselmann
def GetX509CertValidity(cert):
86 c50645c0 Michael Hanselmann
  """Returns the validity period of the certificate.
87 c50645c0 Michael Hanselmann

88 c50645c0 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
89 c50645c0 Michael Hanselmann
  @param cert: X509 certificate object
90 c50645c0 Michael Hanselmann

91 c50645c0 Michael Hanselmann
  """
92 c50645c0 Michael Hanselmann
  # The get_notBefore and get_notAfter functions are only supported in
93 c50645c0 Michael Hanselmann
  # pyOpenSSL 0.7 and above.
94 c50645c0 Michael Hanselmann
  try:
95 c50645c0 Michael Hanselmann
    get_notbefore_fn = cert.get_notBefore
96 c50645c0 Michael Hanselmann
  except AttributeError:
97 c50645c0 Michael Hanselmann
    not_before = None
98 c50645c0 Michael Hanselmann
  else:
99 c50645c0 Michael Hanselmann
    not_before_asn1 = get_notbefore_fn()
100 c50645c0 Michael Hanselmann
101 c50645c0 Michael Hanselmann
    if not_before_asn1 is None:
102 c50645c0 Michael Hanselmann
      not_before = None
103 c50645c0 Michael Hanselmann
    else:
104 c50645c0 Michael Hanselmann
      not_before = _ParseAsn1Generalizedtime(not_before_asn1)
105 c50645c0 Michael Hanselmann
106 c50645c0 Michael Hanselmann
  try:
107 c50645c0 Michael Hanselmann
    get_notafter_fn = cert.get_notAfter
108 c50645c0 Michael Hanselmann
  except AttributeError:
109 c50645c0 Michael Hanselmann
    not_after = None
110 c50645c0 Michael Hanselmann
  else:
111 c50645c0 Michael Hanselmann
    not_after_asn1 = get_notafter_fn()
112 c50645c0 Michael Hanselmann
113 c50645c0 Michael Hanselmann
    if not_after_asn1 is None:
114 c50645c0 Michael Hanselmann
      not_after = None
115 c50645c0 Michael Hanselmann
    else:
116 c50645c0 Michael Hanselmann
      not_after = _ParseAsn1Generalizedtime(not_after_asn1)
117 c50645c0 Michael Hanselmann
118 c50645c0 Michael Hanselmann
  return (not_before, not_after)
119 c50645c0 Michael Hanselmann
120 c50645c0 Michael Hanselmann
121 c50645c0 Michael Hanselmann
def _VerifyCertificateInner(expired, not_before, not_after, now,
122 c50645c0 Michael Hanselmann
                            warn_days, error_days):
123 c50645c0 Michael Hanselmann
  """Verifies certificate validity.
124 c50645c0 Michael Hanselmann

125 c50645c0 Michael Hanselmann
  @type expired: bool
126 c50645c0 Michael Hanselmann
  @param expired: Whether pyOpenSSL considers the certificate as expired
127 c50645c0 Michael Hanselmann
  @type not_before: number or None
128 c50645c0 Michael Hanselmann
  @param not_before: Unix timestamp before which certificate is not valid
129 c50645c0 Michael Hanselmann
  @type not_after: number or None
130 c50645c0 Michael Hanselmann
  @param not_after: Unix timestamp after which certificate is invalid
131 c50645c0 Michael Hanselmann
  @type now: number
132 c50645c0 Michael Hanselmann
  @param now: Current time as Unix timestamp
133 c50645c0 Michael Hanselmann
  @type warn_days: number or None
134 c50645c0 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
135 c50645c0 Michael Hanselmann
  @type error_days: number or None
136 c50645c0 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
137 c50645c0 Michael Hanselmann

138 c50645c0 Michael Hanselmann
  """
139 c50645c0 Michael Hanselmann
  if expired:
140 c50645c0 Michael Hanselmann
    msg = "Certificate is expired"
141 c50645c0 Michael Hanselmann
142 c50645c0 Michael Hanselmann
    if not_before is not None and not_after is not None:
143 c50645c0 Michael Hanselmann
      msg += (" (valid from %s to %s)" %
144 c50645c0 Michael Hanselmann
              (utils_text.FormatTime(not_before),
145 c50645c0 Michael Hanselmann
               utils_text.FormatTime(not_after)))
146 c50645c0 Michael Hanselmann
    elif not_before is not None:
147 c50645c0 Michael Hanselmann
      msg += " (valid from %s)" % utils_text.FormatTime(not_before)
148 c50645c0 Michael Hanselmann
    elif not_after is not None:
149 c50645c0 Michael Hanselmann
      msg += " (valid until %s)" % utils_text.FormatTime(not_after)
150 c50645c0 Michael Hanselmann
151 c50645c0 Michael Hanselmann
    return (CERT_ERROR, msg)
152 c50645c0 Michael Hanselmann
153 c50645c0 Michael Hanselmann
  elif not_before is not None and not_before > now:
154 c50645c0 Michael Hanselmann
    return (CERT_WARNING,
155 c50645c0 Michael Hanselmann
            "Certificate not yet valid (valid from %s)" %
156 c50645c0 Michael Hanselmann
            utils_text.FormatTime(not_before))
157 c50645c0 Michael Hanselmann
158 c50645c0 Michael Hanselmann
  elif not_after is not None:
159 c50645c0 Michael Hanselmann
    remaining_days = int((not_after - now) / (24 * 3600))
160 c50645c0 Michael Hanselmann
161 c50645c0 Michael Hanselmann
    msg = "Certificate expires in about %d days" % remaining_days
162 c50645c0 Michael Hanselmann
163 c50645c0 Michael Hanselmann
    if error_days is not None and remaining_days <= error_days:
164 c50645c0 Michael Hanselmann
      return (CERT_ERROR, msg)
165 c50645c0 Michael Hanselmann
166 c50645c0 Michael Hanselmann
    if warn_days is not None and remaining_days <= warn_days:
167 c50645c0 Michael Hanselmann
      return (CERT_WARNING, msg)
168 c50645c0 Michael Hanselmann
169 c50645c0 Michael Hanselmann
  return (None, None)
170 c50645c0 Michael Hanselmann
171 c50645c0 Michael Hanselmann
172 c50645c0 Michael Hanselmann
def VerifyX509Certificate(cert, warn_days, error_days):
173 a3d32770 Iustin Pop
  """Verifies a certificate for LUClusterVerify.
174 c50645c0 Michael Hanselmann

175 c50645c0 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
176 c50645c0 Michael Hanselmann
  @param cert: X509 certificate object
177 c50645c0 Michael Hanselmann
  @type warn_days: number or None
178 c50645c0 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
179 c50645c0 Michael Hanselmann
  @type error_days: number or None
180 c50645c0 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
181 c50645c0 Michael Hanselmann

182 c50645c0 Michael Hanselmann
  """
183 c50645c0 Michael Hanselmann
  # Depending on the pyOpenSSL version, this can just return (None, None)
184 c50645c0 Michael Hanselmann
  (not_before, not_after) = GetX509CertValidity(cert)
185 c50645c0 Michael Hanselmann
186 f97a7ada Iustin Pop
  now = time.time() + constants.NODE_MAX_CLOCK_SKEW
187 f97a7ada Iustin Pop
188 c50645c0 Michael Hanselmann
  return _VerifyCertificateInner(cert.has_expired(), not_before, not_after,
189 f97a7ada Iustin Pop
                                 now, warn_days, error_days)
190 c50645c0 Michael Hanselmann
191 c50645c0 Michael Hanselmann
192 c50645c0 Michael Hanselmann
def SignX509Certificate(cert, key, salt):
193 c50645c0 Michael Hanselmann
  """Sign a X509 certificate.
194 c50645c0 Michael Hanselmann

195 c50645c0 Michael Hanselmann
  An RFC822-like signature header is added in front of the certificate.
196 c50645c0 Michael Hanselmann

197 c50645c0 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
198 c50645c0 Michael Hanselmann
  @param cert: X509 certificate object
199 c50645c0 Michael Hanselmann
  @type key: string
200 c50645c0 Michael Hanselmann
  @param key: Key for HMAC
201 c50645c0 Michael Hanselmann
  @type salt: string
202 c50645c0 Michael Hanselmann
  @param salt: Salt for HMAC
203 c50645c0 Michael Hanselmann
  @rtype: string
204 c50645c0 Michael Hanselmann
  @return: Serialized and signed certificate in PEM format
205 c50645c0 Michael Hanselmann

206 c50645c0 Michael Hanselmann
  """
207 c50645c0 Michael Hanselmann
  if not VALID_X509_SIGNATURE_SALT.match(salt):
208 c50645c0 Michael Hanselmann
    raise errors.GenericError("Invalid salt: %r" % salt)
209 c50645c0 Michael Hanselmann
210 c50645c0 Michael Hanselmann
  # Dumping as PEM here ensures the certificate is in a sane format
211 c50645c0 Michael Hanselmann
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
212 c50645c0 Michael Hanselmann
213 c50645c0 Michael Hanselmann
  return ("%s: %s/%s\n\n%s" %
214 c50645c0 Michael Hanselmann
          (constants.X509_CERT_SIGNATURE_HEADER, salt,
215 c50645c0 Michael Hanselmann
           utils_hash.Sha1Hmac(key, cert_pem, salt=salt),
216 c50645c0 Michael Hanselmann
           cert_pem))
217 c50645c0 Michael Hanselmann
218 c50645c0 Michael Hanselmann
219 c50645c0 Michael Hanselmann
def _ExtractX509CertificateSignature(cert_pem):
220 c50645c0 Michael Hanselmann
  """Helper function to extract signature from X509 certificate.
221 c50645c0 Michael Hanselmann

222 c50645c0 Michael Hanselmann
  """
223 c50645c0 Michael Hanselmann
  # Extract signature from original PEM data
224 c50645c0 Michael Hanselmann
  for line in cert_pem.splitlines():
225 c50645c0 Michael Hanselmann
    if line.startswith("---"):
226 c50645c0 Michael Hanselmann
      break
227 c50645c0 Michael Hanselmann
228 c50645c0 Michael Hanselmann
    m = X509_SIGNATURE.match(line.strip())
229 c50645c0 Michael Hanselmann
    if m:
230 c50645c0 Michael Hanselmann
      return (m.group("salt"), m.group("sign"))
231 c50645c0 Michael Hanselmann
232 c50645c0 Michael Hanselmann
  raise errors.GenericError("X509 certificate signature is missing")
233 c50645c0 Michael Hanselmann
234 c50645c0 Michael Hanselmann
235 c50645c0 Michael Hanselmann
def LoadSignedX509Certificate(cert_pem, key):
236 c50645c0 Michael Hanselmann
  """Verifies a signed X509 certificate.
237 c50645c0 Michael Hanselmann

238 c50645c0 Michael Hanselmann
  @type cert_pem: string
239 c50645c0 Michael Hanselmann
  @param cert_pem: Certificate in PEM format and with signature header
240 c50645c0 Michael Hanselmann
  @type key: string
241 c50645c0 Michael Hanselmann
  @param key: Key for HMAC
242 c50645c0 Michael Hanselmann
  @rtype: tuple; (OpenSSL.crypto.X509, string)
243 c50645c0 Michael Hanselmann
  @return: X509 certificate object and salt
244 c50645c0 Michael Hanselmann

245 c50645c0 Michael Hanselmann
  """
246 c50645c0 Michael Hanselmann
  (salt, signature) = _ExtractX509CertificateSignature(cert_pem)
247 c50645c0 Michael Hanselmann
248 6b96df59 Michael Hanselmann
  # Load and dump certificate to ensure it's in a sane format
249 6b96df59 Michael Hanselmann
  (cert, sane_pem) = ExtractX509Certificate(cert_pem)
250 c50645c0 Michael Hanselmann
251 c50645c0 Michael Hanselmann
  if not utils_hash.VerifySha1Hmac(key, sane_pem, signature, salt=salt):
252 c50645c0 Michael Hanselmann
    raise errors.GenericError("X509 certificate signature is invalid")
253 c50645c0 Michael Hanselmann
254 c50645c0 Michael Hanselmann
  return (cert, salt)
255 c50645c0 Michael Hanselmann
256 c50645c0 Michael Hanselmann
257 c50645c0 Michael Hanselmann
def GenerateSelfSignedX509Cert(common_name, validity):
258 c50645c0 Michael Hanselmann
  """Generates a self-signed X509 certificate.
259 c50645c0 Michael Hanselmann

260 c50645c0 Michael Hanselmann
  @type common_name: string
261 c50645c0 Michael Hanselmann
  @param common_name: commonName value
262 c50645c0 Michael Hanselmann
  @type validity: int
263 c50645c0 Michael Hanselmann
  @param validity: Validity for certificate in seconds
264 b6267745 Andrea Spadaccini
  @return: a tuple of strings containing the PEM-encoded private key and
265 b6267745 Andrea Spadaccini
           certificate
266 c50645c0 Michael Hanselmann

267 c50645c0 Michael Hanselmann
  """
268 c50645c0 Michael Hanselmann
  # Create private and public key
269 c50645c0 Michael Hanselmann
  key = OpenSSL.crypto.PKey()
270 c50645c0 Michael Hanselmann
  key.generate_key(OpenSSL.crypto.TYPE_RSA, constants.RSA_KEY_BITS)
271 c50645c0 Michael Hanselmann
272 c50645c0 Michael Hanselmann
  # Create self-signed certificate
273 c50645c0 Michael Hanselmann
  cert = OpenSSL.crypto.X509()
274 c50645c0 Michael Hanselmann
  if common_name:
275 c50645c0 Michael Hanselmann
    cert.get_subject().CN = common_name
276 c50645c0 Michael Hanselmann
  cert.set_serial_number(1)
277 c50645c0 Michael Hanselmann
  cert.gmtime_adj_notBefore(0)
278 c50645c0 Michael Hanselmann
  cert.gmtime_adj_notAfter(validity)
279 c50645c0 Michael Hanselmann
  cert.set_issuer(cert.get_subject())
280 c50645c0 Michael Hanselmann
  cert.set_pubkey(key)
281 c50645c0 Michael Hanselmann
  cert.sign(key, constants.X509_CERT_SIGN_DIGEST)
282 c50645c0 Michael Hanselmann
283 c50645c0 Michael Hanselmann
  key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
284 c50645c0 Michael Hanselmann
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
285 c50645c0 Michael Hanselmann
286 c50645c0 Michael Hanselmann
  return (key_pem, cert_pem)
287 c50645c0 Michael Hanselmann
288 c50645c0 Michael Hanselmann
289 c50645c0 Michael Hanselmann
def GenerateSelfSignedSslCert(filename, common_name=constants.X509_CERT_CN,
290 c50645c0 Michael Hanselmann
                              validity=constants.X509_CERT_DEFAULT_VALIDITY):
291 c50645c0 Michael Hanselmann
  """Legacy function to generate self-signed X509 certificate.
292 c50645c0 Michael Hanselmann

293 c50645c0 Michael Hanselmann
  @type filename: str
294 c50645c0 Michael Hanselmann
  @param filename: path to write certificate to
295 c50645c0 Michael Hanselmann
  @type common_name: string
296 c50645c0 Michael Hanselmann
  @param common_name: commonName value
297 c50645c0 Michael Hanselmann
  @type validity: int
298 c50645c0 Michael Hanselmann
  @param validity: validity of certificate in number of days
299 b6267745 Andrea Spadaccini
  @return: a tuple of strings containing the PEM-encoded private key and
300 b6267745 Andrea Spadaccini
           certificate
301 c50645c0 Michael Hanselmann

302 c50645c0 Michael Hanselmann
  """
303 c50645c0 Michael Hanselmann
  # TODO: Investigate using the cluster name instead of X505_CERT_CN for
304 c50645c0 Michael Hanselmann
  # common_name, as cluster-renames are very seldom, and it'd be nice if RAPI
305 c50645c0 Michael Hanselmann
  # and node daemon certificates have the proper Subject/Issuer.
306 c50645c0 Michael Hanselmann
  (key_pem, cert_pem) = GenerateSelfSignedX509Cert(common_name,
307 c50645c0 Michael Hanselmann
                                                   validity * 24 * 60 * 60)
308 c50645c0 Michael Hanselmann
309 c50645c0 Michael Hanselmann
  utils_io.WriteFile(filename, mode=0400, data=key_pem + cert_pem)
310 b6267745 Andrea Spadaccini
  return (key_pem, cert_pem)
311 6b96df59 Michael Hanselmann
312 6b96df59 Michael Hanselmann
313 6b96df59 Michael Hanselmann
def ExtractX509Certificate(pem):
314 6b96df59 Michael Hanselmann
  """Extracts the certificate from a PEM-formatted string.
315 6b96df59 Michael Hanselmann

316 6b96df59 Michael Hanselmann
  @type pem: string
317 6b96df59 Michael Hanselmann
  @rtype: tuple; (OpenSSL.X509 object, string)
318 6b96df59 Michael Hanselmann
  @return: Certificate object and PEM-formatted certificate
319 6b96df59 Michael Hanselmann

320 6b96df59 Michael Hanselmann
  """
321 6b96df59 Michael Hanselmann
  cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, pem)
322 6b96df59 Michael Hanselmann
323 6b96df59 Michael Hanselmann
  return (cert,
324 6b96df59 Michael Hanselmann
          OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert))
325 a8b3b09d Michael Hanselmann
326 a8b3b09d Michael Hanselmann
327 a8b3b09d Michael Hanselmann
def PrepareX509CertKeyCheck(cert, key):
328 a8b3b09d Michael Hanselmann
  """Get function for verifying certificate with a certain private key.
329 a8b3b09d Michael Hanselmann

330 a8b3b09d Michael Hanselmann
  @type key: OpenSSL.crypto.PKey
331 a8b3b09d Michael Hanselmann
  @param key: Private key object
332 a8b3b09d Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
333 a8b3b09d Michael Hanselmann
  @param cert: X509 certificate object
334 a8b3b09d Michael Hanselmann
  @rtype: callable
335 a8b3b09d Michael Hanselmann
  @return: Callable doing the actual check; will raise C{OpenSSL.SSL.Error} if
336 a8b3b09d Michael Hanselmann
    certificate is not signed by given private key
337 a8b3b09d Michael Hanselmann

338 a8b3b09d Michael Hanselmann
  """
339 a8b3b09d Michael Hanselmann
  ctx = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD)
340 a8b3b09d Michael Hanselmann
  ctx.use_privatekey(key)
341 86b9a385 Michael Hanselmann
  ctx.use_certificate(cert)
342 a8b3b09d Michael Hanselmann
343 a8b3b09d Michael Hanselmann
  return ctx.check_privatekey
344 0602cef3 Michael Hanselmann
345 0602cef3 Michael Hanselmann
346 0602cef3 Michael Hanselmann
def CheckNodeCertificate(cert, _noded_cert_file=pathutils.NODED_CERT_FILE):
347 0602cef3 Michael Hanselmann
  """Checks the local node daemon certificate against given certificate.
348 0602cef3 Michael Hanselmann

349 0602cef3 Michael Hanselmann
  Both certificates must be signed with the same key (as stored in the local
350 0602cef3 Michael Hanselmann
  L{pathutils.NODED_CERT_FILE} file). No error is raised if no local
351 0602cef3 Michael Hanselmann
  certificate can be found.
352 0602cef3 Michael Hanselmann

353 0602cef3 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
354 0602cef3 Michael Hanselmann
  @param cert: X509 certificate object
355 0602cef3 Michael Hanselmann
  @raise errors.X509CertError: When an error related to X509 occurred
356 0602cef3 Michael Hanselmann
  @raise errors.GenericError: When the verification failed
357 0602cef3 Michael Hanselmann

358 0602cef3 Michael Hanselmann
  """
359 0602cef3 Michael Hanselmann
  try:
360 0602cef3 Michael Hanselmann
    noded_pem = utils_io.ReadFile(_noded_cert_file)
361 0602cef3 Michael Hanselmann
  except EnvironmentError, err:
362 0602cef3 Michael Hanselmann
    if err.errno != errno.ENOENT:
363 0602cef3 Michael Hanselmann
      raise
364 0602cef3 Michael Hanselmann
365 0602cef3 Michael Hanselmann
    logging.debug("Node certificate file '%s' was not found", _noded_cert_file)
366 0602cef3 Michael Hanselmann
    return
367 0602cef3 Michael Hanselmann
368 0602cef3 Michael Hanselmann
  try:
369 0602cef3 Michael Hanselmann
    noded_cert = \
370 0602cef3 Michael Hanselmann
      OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, noded_pem)
371 0602cef3 Michael Hanselmann
  except Exception, err:
372 0602cef3 Michael Hanselmann
    raise errors.X509CertError(_noded_cert_file,
373 0602cef3 Michael Hanselmann
                               "Unable to load certificate: %s" % err)
374 0602cef3 Michael Hanselmann
375 0602cef3 Michael Hanselmann
  try:
376 0602cef3 Michael Hanselmann
    noded_key = \
377 0602cef3 Michael Hanselmann
      OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, noded_pem)
378 0602cef3 Michael Hanselmann
  except Exception, err:
379 0602cef3 Michael Hanselmann
    raise errors.X509CertError(_noded_cert_file,
380 0602cef3 Michael Hanselmann
                               "Unable to load private key: %s" % err)
381 0602cef3 Michael Hanselmann
382 0602cef3 Michael Hanselmann
  # Check consistency of server.pem file
383 0602cef3 Michael Hanselmann
  check_fn = PrepareX509CertKeyCheck(noded_cert, noded_key)
384 0602cef3 Michael Hanselmann
  try:
385 0602cef3 Michael Hanselmann
    check_fn()
386 0602cef3 Michael Hanselmann
  except OpenSSL.SSL.Error:
387 0602cef3 Michael Hanselmann
    # This should never happen as it would mean the certificate in server.pem
388 0602cef3 Michael Hanselmann
    # is out of sync with the private key stored in the same file
389 0602cef3 Michael Hanselmann
    raise errors.X509CertError(_noded_cert_file,
390 0602cef3 Michael Hanselmann
                               "Certificate does not match with private key")
391 0602cef3 Michael Hanselmann
392 0602cef3 Michael Hanselmann
  # Check with supplied certificate with local key
393 0602cef3 Michael Hanselmann
  check_fn = PrepareX509CertKeyCheck(cert, noded_key)
394 0602cef3 Michael Hanselmann
  try:
395 0602cef3 Michael Hanselmann
    check_fn()
396 0602cef3 Michael Hanselmann
  except OpenSSL.SSL.Error:
397 0602cef3 Michael Hanselmann
    raise errors.GenericError("Given cluster certificate does not match"
398 0602cef3 Michael Hanselmann
                              " local key")