Skip to content

Commit 73cf3b6

Browse files
Fixed bug connecting to databases with older 11g password verifiers
(#189).
1 parent 6567400 commit 73cf3b6

File tree

2 files changed

+37
-26
lines changed

2 files changed

+37
-26
lines changed

doc/src/release_notes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Thin Mode Changes
2020
:data:`ConnectionPool.timeout` seconds.
2121
#) Fixed bug using :attr:`Cursor.arraysize` for tuning data fetches from REF
2222
CURSORS.
23+
#) Fixed bug connecting to databases with older 11g password verifiers
24+
(`issue 189 <https://github.com/oracle/python-oracledb/issues/189>`__).
2325
#) Fixed bugs in the implementation of the statement cache.
2426

2527
Thick Mode Changes

src/oracledb/impl/thin/messages.pyx

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,20 +1325,18 @@ cdef class AuthMessage(Message):
13251325
newpassword_with_salt)
13261326
self.encoded_newpassword = encrypted_newpassword.hex().upper()
13271327

1328-
cdef int _generate_verifier(self, bint verifier_11g) except -1:
1328+
cdef int _generate_verifier(self) except -1:
13291329
"""
13301330
Generate the multi-round verifier.
13311331
"""
1332-
cdef bytes jdwp_data
1332+
cdef:
1333+
bytes jdwp_data
1334+
bytearray b
1335+
ssize_t i
13331336

13341337
# create password hash
13351338
verifier_data = bytes.fromhex(self.session_data['AUTH_VFR_DATA'])
1336-
if verifier_11g:
1337-
keylen = 24
1338-
h = hashlib.sha1(self.password)
1339-
h.update(verifier_data)
1340-
password_hash = h.digest() + bytes(4)
1341-
else:
1339+
if self.verifier_type == TNS_VERIFIER_TYPE_12C:
13421340
keylen = 32
13431341
iterations = int(self.session_data['AUTH_PBKDF2_VGEN_COUNT'])
13441342
salt = verifier_data + b'AUTH_PBKDF2_SPEEDY_KEY'
@@ -1348,28 +1346,42 @@ cdef class AuthMessage(Message):
13481346
h.update(password_key)
13491347
h.update(verifier_data)
13501348
password_hash = h.digest()[:32]
1349+
else:
1350+
keylen = 24
1351+
h = hashlib.sha1(self.password)
1352+
h.update(verifier_data)
1353+
password_hash = h.digest() + bytes(4)
13511354

13521355
# decrypt first half of session key
13531356
encoded_server_key = bytes.fromhex(self.session_data['AUTH_SESSKEY'])
13541357
session_key_part_a = decrypt_cbc(password_hash, encoded_server_key)
13551358

13561359
# generate second half of session key
1357-
session_key_part_b = secrets.token_bytes(32)
1360+
session_key_part_b = secrets.token_bytes(len(session_key_part_a))
13581361
encoded_client_key = encrypt_cbc(password_hash, session_key_part_b)
1359-
self.session_key = encoded_client_key.hex().upper()[:64]
13601362

1361-
# create session key from combo key
1362-
mixing_salt = bytes.fromhex(self.session_data['AUTH_PBKDF2_CSK_SALT'])
1363-
iterations = int(self.session_data['AUTH_PBKDF2_SDER_COUNT'])
1364-
temp_key = session_key_part_b[:keylen] + session_key_part_a[:keylen]
1365-
combo_key = get_derived_key(temp_key.hex().upper().encode(),
1366-
mixing_salt, keylen, iterations)
1363+
# create session key and combo key
1364+
if len(session_key_part_a) == 48:
1365+
self.session_key = encoded_client_key.hex().upper()[:96]
1366+
b = bytearray(24)
1367+
for i in range(16, 40):
1368+
b[i - 16] = session_key_part_a[i] ^ session_key_part_b[i]
1369+
part1 = hashlib.md5(b[:16]).digest()
1370+
part2 = hashlib.md5(b[16:]).digest()
1371+
combo_key = (part1 + part2)[:keylen]
1372+
else:
1373+
self.session_key = encoded_client_key.hex().upper()[:64]
1374+
salt = bytes.fromhex(self.session_data['AUTH_PBKDF2_CSK_SALT'])
1375+
iterations = int(self.session_data['AUTH_PBKDF2_SDER_COUNT'])
1376+
temp_key = session_key_part_b[:keylen] + session_key_part_a[:keylen]
1377+
combo_key = get_derived_key(temp_key.hex().upper().encode(), salt,
1378+
keylen, iterations)
13671379

13681380
# retain session key for use by the change password API
13691381
self.conn_impl._combo_key = combo_key
13701382

13711383
# generate speedy key for 12c verifiers
1372-
if not verifier_11g:
1384+
if self.verifier_type == TNS_VERIFIER_TYPE_12C:
13731385
salt = secrets.token_bytes(16)
13741386
speedy_key = encrypt_cbc(combo_key, salt + password_key)
13751387
self.speedy_key = speedy_key[:80].hex().upper()
@@ -1539,7 +1551,6 @@ cdef class AuthMessage(Message):
15391551
cdef int _write_message(self, WriteBuffer buf) except -1:
15401552
cdef:
15411553
uint8_t has_user = 1 if self.user_bytes_len > 0 else 0
1542-
bint verifier_11g = False
15431554
uint32_t num_pairs
15441555

15451556
# perform final determination of data to write
@@ -1559,15 +1570,13 @@ cdef class AuthMessage(Message):
15591570
else:
15601571
num_pairs += 2
15611572
self.auth_mode |= TNS_AUTH_MODE_WITH_PASSWORD
1562-
if self.verifier_type in (TNS_VERIFIER_TYPE_11G_1,
1563-
TNS_VERIFIER_TYPE_11G_2):
1564-
verifier_11g = True
1565-
elif self.verifier_type != TNS_VERIFIER_TYPE_12C:
1573+
if self.verifier_type == TNS_VERIFIER_TYPE_12C:
1574+
num_pairs += 1
1575+
elif self.verifier_type not in (TNS_VERIFIER_TYPE_11G_1,
1576+
TNS_VERIFIER_TYPE_11G_2):
15661577
errors._raise_err(errors.ERR_UNSUPPORTED_VERIFIER_TYPE,
15671578
verifier_type=self.verifier_type)
1568-
else:
1569-
num_pairs += 1
1570-
self._generate_verifier(verifier_11g)
1579+
self._generate_verifier()
15711580

15721581
# determine which other key/value pairs to write
15731582
if self.newpassword is not None:
@@ -1615,7 +1624,7 @@ cdef class AuthMessage(Message):
16151624
self._write_key_value(buf, "AUTH_TOKEN", self.token)
16161625
elif not self.change_password:
16171626
self._write_key_value(buf, "AUTH_SESSKEY", self.session_key, 1)
1618-
if not verifier_11g:
1627+
if self.verifier_type == TNS_VERIFIER_TYPE_12C:
16191628
self._write_key_value(buf, "AUTH_PBKDF2_SPEEDY_KEY",
16201629
self.speedy_key)
16211630
if self.encoded_password is not None:

0 commit comments

Comments
 (0)