Fixed bug connecting to databases with older 11g password verifiers
(#189).
This commit is contained in:
parent
9f7a1c3490
commit
5ec3f5cc30
|
@ -13,6 +13,9 @@ oracledb 1.3.2 (TBD)
|
||||||
Thin Mode Changes
|
Thin Mode Changes
|
||||||
+++++++++++++++++
|
+++++++++++++++++
|
||||||
|
|
||||||
|
#) Fixed bug connecting to databases with older 11g password verifiers
|
||||||
|
(`issue 189 <https://github.com/oracle/python-oracledb/issues/189>`__).
|
||||||
|
|
||||||
Thick Mode Changes
|
Thick Mode Changes
|
||||||
++++++++++++++++++
|
++++++++++++++++++
|
||||||
|
|
||||||
|
|
|
@ -1326,20 +1326,18 @@ cdef class AuthMessage(Message):
|
||||||
newpassword_with_salt)
|
newpassword_with_salt)
|
||||||
self.encoded_newpassword = encrypted_newpassword.hex().upper()
|
self.encoded_newpassword = encrypted_newpassword.hex().upper()
|
||||||
|
|
||||||
cdef int _generate_verifier(self, bint verifier_11g) except -1:
|
cdef int _generate_verifier(self) except -1:
|
||||||
"""
|
"""
|
||||||
Generate the multi-round verifier.
|
Generate the multi-round verifier.
|
||||||
"""
|
"""
|
||||||
cdef bytes jdwp_data
|
cdef:
|
||||||
|
bytes jdwp_data
|
||||||
|
bytearray b
|
||||||
|
ssize_t i
|
||||||
|
|
||||||
# create password hash
|
# create password hash
|
||||||
verifier_data = bytes.fromhex(self.session_data['AUTH_VFR_DATA'])
|
verifier_data = bytes.fromhex(self.session_data['AUTH_VFR_DATA'])
|
||||||
if verifier_11g:
|
if self.verifier_type == TNS_VERIFIER_TYPE_12C:
|
||||||
keylen = 24
|
|
||||||
h = hashlib.sha1(self.password)
|
|
||||||
h.update(verifier_data)
|
|
||||||
password_hash = h.digest() + bytes(4)
|
|
||||||
else:
|
|
||||||
keylen = 32
|
keylen = 32
|
||||||
iterations = int(self.session_data['AUTH_PBKDF2_VGEN_COUNT'])
|
iterations = int(self.session_data['AUTH_PBKDF2_VGEN_COUNT'])
|
||||||
salt = verifier_data + b'AUTH_PBKDF2_SPEEDY_KEY'
|
salt = verifier_data + b'AUTH_PBKDF2_SPEEDY_KEY'
|
||||||
|
@ -1349,28 +1347,42 @@ cdef class AuthMessage(Message):
|
||||||
h.update(password_key)
|
h.update(password_key)
|
||||||
h.update(verifier_data)
|
h.update(verifier_data)
|
||||||
password_hash = h.digest()[:32]
|
password_hash = h.digest()[:32]
|
||||||
|
else:
|
||||||
|
keylen = 24
|
||||||
|
h = hashlib.sha1(self.password)
|
||||||
|
h.update(verifier_data)
|
||||||
|
password_hash = h.digest() + bytes(4)
|
||||||
|
|
||||||
# decrypt first half of session key
|
# decrypt first half of session key
|
||||||
encoded_server_key = bytes.fromhex(self.session_data['AUTH_SESSKEY'])
|
encoded_server_key = bytes.fromhex(self.session_data['AUTH_SESSKEY'])
|
||||||
session_key_part_a = decrypt_cbc(password_hash, encoded_server_key)
|
session_key_part_a = decrypt_cbc(password_hash, encoded_server_key)
|
||||||
|
|
||||||
# generate second half of session key
|
# generate second half of session key
|
||||||
session_key_part_b = secrets.token_bytes(32)
|
session_key_part_b = secrets.token_bytes(len(session_key_part_a))
|
||||||
encoded_client_key = encrypt_cbc(password_hash, session_key_part_b)
|
encoded_client_key = encrypt_cbc(password_hash, session_key_part_b)
|
||||||
self.session_key = encoded_client_key.hex().upper()[:64]
|
|
||||||
|
|
||||||
# create session key from combo key
|
# create session key and combo key
|
||||||
mixing_salt = bytes.fromhex(self.session_data['AUTH_PBKDF2_CSK_SALT'])
|
if len(session_key_part_a) == 48:
|
||||||
|
self.session_key = encoded_client_key.hex().upper()[:96]
|
||||||
|
b = bytearray(24)
|
||||||
|
for i in range(16, 40):
|
||||||
|
b[i - 16] = session_key_part_a[i] ^ session_key_part_b[i]
|
||||||
|
part1 = hashlib.md5(b[:16]).digest()
|
||||||
|
part2 = hashlib.md5(b[16:]).digest()
|
||||||
|
combo_key = (part1 + part2)[:keylen]
|
||||||
|
else:
|
||||||
|
self.session_key = encoded_client_key.hex().upper()[:64]
|
||||||
|
salt = bytes.fromhex(self.session_data['AUTH_PBKDF2_CSK_SALT'])
|
||||||
iterations = int(self.session_data['AUTH_PBKDF2_SDER_COUNT'])
|
iterations = int(self.session_data['AUTH_PBKDF2_SDER_COUNT'])
|
||||||
temp_key = session_key_part_b[:keylen] + session_key_part_a[:keylen]
|
temp_key = session_key_part_b[:keylen] + session_key_part_a[:keylen]
|
||||||
combo_key = get_derived_key(temp_key.hex().upper().encode(),
|
combo_key = get_derived_key(temp_key.hex().upper().encode(), salt,
|
||||||
mixing_salt, keylen, iterations)
|
keylen, iterations)
|
||||||
|
|
||||||
# retain session key for use by the change password API
|
# retain session key for use by the change password API
|
||||||
self.conn_impl._combo_key = combo_key
|
self.conn_impl._combo_key = combo_key
|
||||||
|
|
||||||
# generate speedy key for 12c verifiers
|
# generate speedy key for 12c verifiers
|
||||||
if not verifier_11g:
|
if self.verifier_type == TNS_VERIFIER_TYPE_12C:
|
||||||
salt = secrets.token_bytes(16)
|
salt = secrets.token_bytes(16)
|
||||||
speedy_key = encrypt_cbc(combo_key, salt + password_key)
|
speedy_key = encrypt_cbc(combo_key, salt + password_key)
|
||||||
self.speedy_key = speedy_key[:80].hex().upper()
|
self.speedy_key = speedy_key[:80].hex().upper()
|
||||||
|
@ -1538,7 +1550,6 @@ cdef class AuthMessage(Message):
|
||||||
cdef int _write_message(self, WriteBuffer buf) except -1:
|
cdef int _write_message(self, WriteBuffer buf) except -1:
|
||||||
cdef:
|
cdef:
|
||||||
uint8_t has_user = 1 if self.user_bytes_len > 0 else 0
|
uint8_t has_user = 1 if self.user_bytes_len > 0 else 0
|
||||||
bint verifier_11g = False
|
|
||||||
uint32_t num_pairs
|
uint32_t num_pairs
|
||||||
|
|
||||||
# perform final determination of data to write
|
# perform final determination of data to write
|
||||||
|
@ -1558,15 +1569,13 @@ cdef class AuthMessage(Message):
|
||||||
else:
|
else:
|
||||||
num_pairs += 2
|
num_pairs += 2
|
||||||
self.auth_mode |= TNS_AUTH_MODE_WITH_PASSWORD
|
self.auth_mode |= TNS_AUTH_MODE_WITH_PASSWORD
|
||||||
if self.verifier_type in (TNS_VERIFIER_TYPE_11G_1,
|
if self.verifier_type == TNS_VERIFIER_TYPE_12C:
|
||||||
|
num_pairs += 1
|
||||||
|
elif self.verifier_type not in (TNS_VERIFIER_TYPE_11G_1,
|
||||||
TNS_VERIFIER_TYPE_11G_2):
|
TNS_VERIFIER_TYPE_11G_2):
|
||||||
verifier_11g = True
|
|
||||||
elif self.verifier_type != TNS_VERIFIER_TYPE_12C:
|
|
||||||
errors._raise_err(errors.ERR_UNSUPPORTED_VERIFIER_TYPE,
|
errors._raise_err(errors.ERR_UNSUPPORTED_VERIFIER_TYPE,
|
||||||
verifier_type=self.verifier_type)
|
verifier_type=self.verifier_type)
|
||||||
else:
|
self._generate_verifier()
|
||||||
num_pairs += 1
|
|
||||||
self._generate_verifier(verifier_11g)
|
|
||||||
|
|
||||||
# determine which other key/value pairs to write
|
# determine which other key/value pairs to write
|
||||||
if self.newpassword is not None:
|
if self.newpassword is not None:
|
||||||
|
@ -1614,7 +1623,7 @@ cdef class AuthMessage(Message):
|
||||||
self._write_key_value(buf, "AUTH_TOKEN", self.token)
|
self._write_key_value(buf, "AUTH_TOKEN", self.token)
|
||||||
elif not self.change_password:
|
elif not self.change_password:
|
||||||
self._write_key_value(buf, "AUTH_SESSKEY", self.session_key, 1)
|
self._write_key_value(buf, "AUTH_SESSKEY", self.session_key, 1)
|
||||||
if not verifier_11g:
|
if self.verifier_type == TNS_VERIFIER_TYPE_12C:
|
||||||
self._write_key_value(buf, "AUTH_PBKDF2_SPEEDY_KEY",
|
self._write_key_value(buf, "AUTH_PBKDF2_SPEEDY_KEY",
|
||||||
self.speedy_key)
|
self.speedy_key)
|
||||||
if self.encoded_password is not None:
|
if self.encoded_password is not None:
|
||||||
|
|
Loading…
Reference in New Issue