Revision 68857643 lib/utils.py

b/lib/utils.py
47 47
import OpenSSL
48 48
import datetime
49 49
import calendar
50
import hmac
50 51

  
51 52
from cStringIO import StringIO
52 53

  
53 54
try:
54 55
  from hashlib import sha1
55 56
except ImportError:
56
  import sha
57
  sha1 = sha.new
57
  import sha as sha1
58 58

  
59 59
from ganeti import errors
60 60
from ganeti import constants
......
70 70

  
71 71
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
72 72

  
73
HEX_CHAR_RE = r"[a-zA-Z0-9]"
74
VALID_X509_SIGNATURE_SALT = re.compile("^%s+$" % HEX_CHAR_RE, re.S)
75
X509_SIGNATURE = re.compile(r"^%s:\s*(?P<salt>%s+)/(?P<sign>%s+)$" %
76
                            (re.escape(constants.X509_CERT_SIGNATURE_HEADER),
77
                             HEX_CHAR_RE, HEX_CHAR_RE),
78
                            re.S | re.I)
79

  
73 80

  
74 81
class RunResult(object):
75 82
  """Holds the result of running external programs.
......
673 680

  
674 681
  f = open(filename)
675 682

  
676
  fp = sha1()
683
  if callable(sha1):
684
    fp = sha1()
685
  else:
686
    fp = sha1.new()
677 687
  while True:
678 688
    data = f.read(4096)
679 689
    if not data:
......
2356 2366
  return (not_before, not_after)
2357 2367

  
2358 2368

  
2369
def SignX509Certificate(cert, key, salt):
2370
  """Sign a X509 certificate.
2371

  
2372
  An RFC822-like signature header is added in front of the certificate.
2373

  
2374
  @type cert: OpenSSL.crypto.X509
2375
  @param cert: X509 certificate object
2376
  @type key: string
2377
  @param key: Key for HMAC
2378
  @type salt: string
2379
  @param salt: Salt for HMAC
2380
  @rtype: string
2381
  @return: Serialized and signed certificate in PEM format
2382

  
2383
  """
2384
  if not VALID_X509_SIGNATURE_SALT.match(salt):
2385
    raise errors.GenericError("Invalid salt: %r" % salt)
2386

  
2387
  # Dumping as PEM here ensures the certificate is in a sane format
2388
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
2389

  
2390
  return ("%s: %s/%s\n\n%s" %
2391
          (constants.X509_CERT_SIGNATURE_HEADER, salt,
2392
           hmac.new(key, salt + cert_pem, sha1).hexdigest(),
2393
           cert_pem))
2394

  
2395

  
2396
def _ExtractX509CertificateSignature(cert_pem):
2397
  """Helper function to extract signature from X509 certificate.
2398

  
2399
  """
2400
  # Extract signature from original PEM data
2401
  for line in cert_pem.splitlines():
2402
    if line.startswith("---"):
2403
      break
2404

  
2405
    m = X509_SIGNATURE.match(line.strip())
2406
    if m:
2407
      return (m.group("salt"), m.group("sign"))
2408

  
2409
  raise errors.GenericError("X509 certificate signature is missing")
2410

  
2411

  
2412
def LoadSignedX509Certificate(cert_pem, key):
2413
  """Verifies a signed X509 certificate.
2414

  
2415
  @type cert_pem: string
2416
  @param cert_pem: Certificate in PEM format and with signature header
2417
  @type key: string
2418
  @param key: Key for HMAC
2419
  @rtype: tuple; (OpenSSL.crypto.X509, string)
2420
  @return: X509 certificate object and salt
2421

  
2422
  """
2423
  (salt, signature) = _ExtractX509CertificateSignature(cert_pem)
2424

  
2425
  # Load certificate
2426
  cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem)
2427

  
2428
  # Dump again to ensure it's in a sane format
2429
  sane_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
2430

  
2431
  if signature != hmac.new(key, salt + sane_pem, sha1).hexdigest():
2432
    raise errors.GenericError("X509 certificate signature is invalid")
2433

  
2434
  return (cert, salt)
2435

  
2436

  
2359 2437
def SafeEncode(text):
2360 2438
  """Return a 'safe' version of a source string.
2361 2439

  

Also available in: Unified diff