Skip to content

Commit 9863bb7

Browse files
tirangraingert
andcommitted
Implement support for OPT_X_TLS_PEERCERT
Co-authored-by: Thomas Grainger <tagrain@gmail.com> Signed-off-by: Christian Heimes <cheimes@redhat.com>
1 parent 84bbf5e commit 9863bb7

File tree

3 files changed

+64
-2
lines changed

3 files changed

+64
-2
lines changed

Doc/reference/ldap.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,12 @@ TLS options
406406

407407
.. py:data:: OPT_X_TLS_PEERCERT
408408
409-
Get peer's certificate as binary ASN.1 data structure (not supported)
409+
Get peer's certificate as binary ASN.1 data structure (DER)
410+
411+
.. versionadded:: 3.4.0
412+
413+
.. note::
414+
The option leaks memory with OpenLDAP < 2.5.8.
410415

411416
.. py:data:: OPT_X_TLS_PROTOCOL_MIN
412417

Modules/options.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "LDAPObject.h"
66
#include "ldapcontrol.h"
77
#include "options.h"
8+
#include "berval.h"
89

910
void
1011
set_timeval_from_double(struct timeval *tv, double d)
@@ -58,6 +59,9 @@ LDAP_set_option(LDAPObject *self, int option, PyObject *value)
5859
case LDAP_OPT_API_FEATURE_INFO:
5960
#ifdef HAVE_SASL
6061
case LDAP_OPT_X_SASL_SSF:
62+
#endif
63+
#ifdef LDAP_OPT_X_TLS_PEERCERT
64+
case LDAP_OPT_X_TLS_PEERCERT:
6165
#endif
6266
/* Read-only options */
6367
PyErr_SetString(PyExc_ValueError, "read-only option");
@@ -256,6 +260,9 @@ LDAP_get_option(LDAPObject *self, int option)
256260
#if HAVE_SASL
257261
/* unsigned long */
258262
ber_len_t blen;
263+
#endif
264+
#ifdef LDAP_OPT_X_TLS_PEERCERT
265+
struct berval bv = {0};
259266
#endif
260267
PyObject *extensions, *v;
261268
Py_ssize_t i, num_extensions;
@@ -424,7 +431,22 @@ LDAP_get_option(LDAPObject *self, int option)
424431
v = LDAPControls_to_List(lcs);
425432
ldap_controls_free(lcs);
426433
return v;
427-
434+
#ifdef LDAP_OPT_X_TLS_PEERCERT
435+
case LDAP_OPT_X_TLS_PEERCERT:
436+
res = LDAP_int_get_option(self, option, &bv);
437+
if (res != LDAP_OPT_SUCCESS) {
438+
return option_error(res, "ldap_get_option");
439+
}
440+
if (bv.bv_len == 0) {
441+
Py_RETURN_NONE;
442+
} else {
443+
v = LDAPberval_to_object(&bv);
444+
/* bv_val memory is allocated with ber_memalloc_x()
445+
* context allocation/dealloc is a private API. */
446+
ber_memfree(bv.bv_val);
447+
return v;
448+
}
449+
#endif
428450
default:
429451
PyErr_Format(PyExc_ValueError, "unknown option %d", option);
430452
return NULL;

Tests/t_ldapobject.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
from slapdtest import requires_ldapi, requires_sasl, requires_tls
2121
from slapdtest import requires_init_fd
2222

23+
try:
24+
from ssl import PEM_cert_to_DER_cert
25+
except ImportError:
26+
PEM_cert_to_DER_cert = None
27+
2328

2429
LDIF_TEMPLATE = """dn: %(suffix)s
2530
objectClass: dcObject
@@ -418,6 +423,36 @@ def test_multiple_starttls(self):
418423
l.simple_bind_s(self.server.root_dn, self.server.root_pw)
419424
self.assertEqual(l.whoami_s(), 'dn:' + self.server.root_dn)
420425

426+
@requires_tls()
427+
@unittest.skipUnless(
428+
hasattr(ldap, "OPT_X_TLS_PEERCERT"),
429+
reason="Requires OPT_X_TLS_PEERCERT"
430+
)
431+
def test_get_tls_peercert(self):
432+
l = self.ldap_object_class(self.server.ldap_uri)
433+
peercert = l.get_option(ldap.OPT_X_TLS_PEERCERT)
434+
self.assertEqual(peercert, None)
435+
with self.assertRaises(ValueError):
436+
l.set_option(ldap.OPT_X_TLS_PEERCERT, b"")
437+
438+
l.set_option(ldap.OPT_X_TLS_CACERTFILE, self.server.cafile)
439+
l.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
440+
l.start_tls_s()
441+
442+
peercert = l.get_option(ldap.OPT_X_TLS_PEERCERT)
443+
self.assertTrue(peercert)
444+
self.assertIsInstance(peercert, bytes)
445+
446+
if PEM_cert_to_DER_cert is not None:
447+
with open(self.server.servercert) as f:
448+
server_pem = f.read()
449+
# remove text
450+
begin = server_pem.find("-----BEGIN CERTIFICATE-----")
451+
server_pem = server_pem[begin:-1]
452+
453+
server_der = PEM_cert_to_DER_cert(server_pem)
454+
self.assertEqual(server_der, peercert)
455+
421456
def test_dse(self):
422457
dse = self._ldap_conn.read_rootdse_s()
423458
self.assertIsInstance(dse, dict)

0 commit comments

Comments
 (0)