Statistics
| Branch: | Tag: | Revision:

root / lib / utils / x509.py @ 653bc0f1

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 ab4b1cf2 Helga Velroyen
def GenerateSelfSignedX509Cert(common_name, validity, serial_no):
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 ab4b1cf2 Helga Velroyen
  cert.set_serial_number(serial_no)
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 ab4b1cf2 Helga Velroyen
def GenerateSelfSignedSslCert(filename, serial_no,
290 ab4b1cf2 Helga Velroyen
                              common_name=constants.X509_CERT_CN,
291 c50645c0 Michael Hanselmann
                              validity=constants.X509_CERT_DEFAULT_VALIDITY):
292 c50645c0 Michael Hanselmann
  """Legacy function to generate self-signed X509 certificate.
293 c50645c0 Michael Hanselmann

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

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

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

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

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

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

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

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

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