Fixes WinRM listener plugin x509 end time

The end date assigned to the x509 certifciate in use for the WinRM
HTTPS listener is not properly computed, resulting in issues during
leap days. This patch solves the issue by employing the appropriate
Win32 API.

Closes-Bug: #1551211

Change-Id: Ib36a2db58634caba4282f19a0386191ca33a4168
This commit is contained in:
Alessandro Pilotti 2016-02-29 14:41:23 +02:00
parent b542bf20e2
commit 7bf618155a
3 changed files with 76 additions and 9 deletions

View File

@ -142,8 +142,9 @@ class CryptoAPICertManagerTests(unittest.TestCase):
generate_key_ret_val=None) generate_key_ret_val=None)
@mock.patch('cloudbaseinit.utils.windows.x509.free') @mock.patch('cloudbaseinit.utils.windows.x509.free')
@mock.patch('copy.copy')
@mock.patch('cloudbaseinit.utils.windows.x509.malloc') @mock.patch('cloudbaseinit.utils.windows.x509.malloc')
@mock.patch('cloudbaseinit.utils.windows.x509.CryptoAPICertManager'
'._add_system_time_interval')
@mock.patch('cloudbaseinit.utils.windows.x509.CryptoAPICertManager' @mock.patch('cloudbaseinit.utils.windows.x509.CryptoAPICertManager'
'._generate_key') '._generate_key')
@mock.patch('cloudbaseinit.utils.windows.x509.CryptoAPICertManager' @mock.patch('cloudbaseinit.utils.windows.x509.CryptoAPICertManager'
@ -185,8 +186,9 @@ class CryptoAPICertManagerTests(unittest.TestCase):
mock_CRYPTOAPI_BLOB, mock_CRYPTOAPI_BLOB,
mock_CertStrToName, mock_CertStrToName,
mock_uuid4, mock_get_cert_thumprint, mock_uuid4, mock_get_cert_thumprint,
mock_generate_key, mock_malloc, mock_generate_key,
mock_copy, mock_free, certstr, mock_add_system_time_interval,
mock_malloc, mock_free, certstr,
certificate, enhanced_key, store_handle, certificate, enhanced_key, store_handle,
context_to_store): context_to_store):
@ -215,7 +217,6 @@ class CryptoAPICertManagerTests(unittest.TestCase):
mock_CRYPT_ALGORITHM_IDENTIFIER.assert_called_once_with() mock_CRYPT_ALGORITHM_IDENTIFIER.assert_called_once_with()
mock_SYSTEMTIME.assert_called_once_with() mock_SYSTEMTIME.assert_called_once_with()
mock_GetSystemTime.assert_called_once_with(mock_byref()) mock_GetSystemTime.assert_called_once_with(mock_byref())
mock_copy.assert_called_once_with(mock_SYSTEMTIME())
mock_CertCreateSelfSignCertificate.assert_called_once_with( mock_CertCreateSelfSignCertificate.assert_called_once_with(
None, mock_byref(), 0, mock_byref(), None, mock_byref(), 0, mock_byref(),
mock_byref(), mock_byref(), mock_byref(), None) mock_byref(), mock_byref(), mock_byref(), None)
@ -228,6 +229,8 @@ class CryptoAPICertManagerTests(unittest.TestCase):
six.text_type(self.x509.STORE_NAME_MY)) six.text_type(self.x509.STORE_NAME_MY))
mock_get_cert_thumprint.assert_called_once_with( mock_get_cert_thumprint.assert_called_once_with(
mock_CertCreateSelfSignCertificate()) mock_CertCreateSelfSignCertificate())
mock_add_system_time_interval.assert_called_once_with(
mock_SYSTEMTIME.return_value, self.x509.X509_END_DATE_INTERVAL)
mock_CertCloseStore.assert_called_once_with(store_handle, 0) mock_CertCloseStore.assert_called_once_with(store_handle, 0)
mock_CertFreeCertificateContext.assert_called_once_with( mock_CertFreeCertificateContext.assert_called_once_with(
@ -238,6 +241,32 @@ class CryptoAPICertManagerTests(unittest.TestCase):
mock_generate_key.assert_called_once_with('fake_name', True) mock_generate_key.assert_called_once_with('fake_name', True)
@mock.patch('cloudbaseinit.utils.windows.cryptoapi.'
'SYSTEMTIME')
@mock.patch('cloudbaseinit.utils.windows.cryptoapi.'
'FILETIME')
@mock.patch('cloudbaseinit.utils.windows.cryptoapi.'
'SystemTimeToFileTime')
@mock.patch('cloudbaseinit.utils.windows.cryptoapi.'
'FileTimeToSystemTime')
def test_add_system_time_interval(self, mock_FileTimeToSystemTime,
mock_SystemTimeToFileTime,
mock_FILETIME, mock_SYSTEMTIME):
mock_system_time = mock.MagicMock()
fake_increment = 1
mock_byref = self._ctypes.byref
new_system_time = self._x509_manager._add_system_time_interval(
mock_system_time, fake_increment)
mock_FILETIME.assert_called_once_with()
mock_SystemTimeToFileTime.assert_called_once_with(mock_byref(),
mock_byref())
mock_SYSTEMTIME.assert_called_once_with()
mock_FileTimeToSystemTime.assert_called_once_with(mock_byref(),
mock_byref())
self.assertEqual(mock_SYSTEMTIME.return_value, new_system_time)
def test_create_self_signed_cert(self): def test_create_self_signed_cert(self):
self._test_create_self_signed_cert(certstr='fake cert name', self._test_create_self_signed_cert(certstr='fake cert name',
certificate='fake certificate', certificate='fake certificate',

View File

@ -42,6 +42,13 @@ class SYSTEMTIME(ctypes.Structure):
] ]
class FILETIME(ctypes.Structure):
_fields_ = [
('dwLowDateTime', wintypes.DWORD),
('dwHighDateTime', wintypes.DWORD),
]
class CERT_CONTEXT(ctypes.Structure): class CERT_CONTEXT(ctypes.Structure):
_fields_ = [ _fields_ = [
('dwCertEncodingType', wintypes.DWORD), ('dwCertEncodingType', wintypes.DWORD),
@ -137,12 +144,22 @@ crypt32.CertStrToNameW.argtypes = [wintypes.DWORD, wintypes.LPCWSTR,
ctypes.POINTER(wintypes.LPCWSTR)] ctypes.POINTER(wintypes.LPCWSTR)]
CertStrToName = crypt32.CertStrToNameW CertStrToName = crypt32.CertStrToNameW
# TODO(alexpilotti): this is not a CryptoAPI funtion, putting it in a separate # TODO(alexpilotti): the following time related functions are not CryptoAPI
# module would be more correct # specific, putting them in a separate module would be more correct
kernel32.GetSystemTime.restype = None kernel32.GetSystemTime.restype = None
kernel32.GetSystemTime.argtypes = [ctypes.POINTER(SYSTEMTIME)] kernel32.GetSystemTime.argtypes = [ctypes.POINTER(SYSTEMTIME)]
GetSystemTime = kernel32.GetSystemTime GetSystemTime = kernel32.GetSystemTime
kernel32.SystemTimeToFileTime.restype = wintypes.BOOL
kernel32.SystemTimeToFileTime.argtypes = [ctypes.POINTER(SYSTEMTIME),
ctypes.POINTER(FILETIME)]
SystemTimeToFileTime = kernel32.SystemTimeToFileTime
kernel32.FileTimeToSystemTime.restype = wintypes.BOOL
kernel32.FileTimeToSystemTime.argtypes = [ctypes.POINTER(FILETIME),
ctypes.POINTER(SYSTEMTIME)]
FileTimeToSystemTime = kernel32.FileTimeToSystemTime
# TODO(alexpilotti): this is not a CryptoAPI funtion, putting it in a separate # TODO(alexpilotti): this is not a CryptoAPI funtion, putting it in a separate
# module would be more correct # module would be more correct
kernel32.GetLastError.restype = wintypes.DWORD kernel32.GetLastError.restype = wintypes.DWORD

View File

@ -13,7 +13,6 @@
# under the License. # under the License.
import copy
import ctypes import ctypes
from ctypes import wintypes from ctypes import wintypes
import uuid import uuid
@ -36,6 +35,8 @@ STORE_NAME_MY = "My"
STORE_NAME_ROOT = "Root" STORE_NAME_ROOT = "Root"
STORE_NAME_TRUSTED_PEOPLE = "TrustedPeople" STORE_NAME_TRUSTED_PEOPLE = "TrustedPeople"
X509_END_DATE_INTERVAL = 10 * 365 * 24 * 60 * 60 * 10000000
class CryptoAPICertManager(object): class CryptoAPICertManager(object):
def _get_cert_thumprint(self, cert_context_p): def _get_cert_thumprint(self, cert_context_p):
@ -107,6 +108,26 @@ class CryptoAPICertManager(object):
if crypt_prov_handle: if crypt_prov_handle:
cryptoapi.CryptReleaseContext(crypt_prov_handle, 0) cryptoapi.CryptReleaseContext(crypt_prov_handle, 0)
@staticmethod
def _add_system_time_interval(system_time, increment):
'''increment's unit: 10ns'''
file_time = cryptoapi.FILETIME()
if not cryptoapi.SystemTimeToFileTime(ctypes.byref(system_time),
ctypes.byref(file_time)):
raise cryptoapi.CryptoAPIException()
t = file_time.dwLowDateTime + (file_time.dwHighDateTime << 32)
t += increment
file_time.dwLowDateTime = t & 0xFFFFFFFF
file_time.dwHighDateTime = t >> 32 & 0xFFFFFFFF
new_system_time = cryptoapi.SYSTEMTIME()
if not cryptoapi.FileTimeToSystemTime(ctypes.byref(file_time),
ctypes.byref(new_system_time)):
raise cryptoapi.CryptoAPIException()
return new_system_time
def create_self_signed_cert(self, subject, validity_years=10, def create_self_signed_cert(self, subject, validity_years=10,
machine_keyset=True, store_name=STORE_NAME_MY): machine_keyset=True, store_name=STORE_NAME_MY):
subject_encoded = None subject_encoded = None
@ -162,8 +183,8 @@ class CryptoAPICertManager(object):
start_time = cryptoapi.SYSTEMTIME() start_time = cryptoapi.SYSTEMTIME()
cryptoapi.GetSystemTime(ctypes.byref(start_time)) cryptoapi.GetSystemTime(ctypes.byref(start_time))
end_time = copy.copy(start_time) end_time = self._add_system_time_interval(
end_time.wYear += validity_years start_time, X509_END_DATE_INTERVAL)
cert_context_p = cryptoapi.CertCreateSelfSignCertificate( cert_context_p = cryptoapi.CertCreateSelfSignCertificate(
None, ctypes.byref(subject_blob), 0, None, ctypes.byref(subject_blob), 0,