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:
parent
b542bf20e2
commit
7bf618155a
@ -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',
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user