Skip to content

Commit 610b2ee

Browse files
tirangraingert
authored 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 e712033 commit 610b2ee

File tree

6 files changed

+68
-2
lines changed

6 files changed

+68
-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.1
412+
413+
.. note::
414+
The option leaks memory with OpenLDAP < 2.5.8.
410415

411416
.. py:data:: OPT_X_TLS_PROTOCOL_MIN
412417

Lib/ldap/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ class Str(Constant):
301301
# Added in OpenLDAP 2.4.52
302302
TLSInt('OPT_X_TLS_REQUIRE_SAN', optional=True),
303303

304+
# Added in OpenLDAP 2.5
305+
TLSInt('OPT_X_TLS_PEERCERT', optional=True),
306+
304307
Int('OPT_X_SASL_MECH'),
305308
Int('OPT_X_SASL_REALM'),
306309
Int('OPT_X_SASL_AUTHCID'),

Modules/berval.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ LDAPberval_to_object(const struct berval *bv)
1717
{
1818
PyObject *ret = NULL;
1919

20-
if (!bv) {
20+
if (!bv || !bv->bv_val) {
2121
ret = Py_None;
2222
Py_INCREF(ret);
2323
}

Modules/constants_generated.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,11 @@ add_int(OPT_X_TLS_PACKAGE);
263263
add_int(OPT_X_TLS_REQUIRE_SAN);
264264
#endif
265265

266+
267+
#if defined(LDAP_OPT_X_TLS_PEERCERT)
268+
add_int(OPT_X_TLS_PEERCERT);
269+
#endif
270+
266271
#endif
267272

268273
add_int(OPT_X_SASL_MECH);

Modules/options.c

Lines changed: 18 additions & 0 deletions
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");
@@ -254,6 +258,7 @@ LDAP_get_option(LDAPObject *self, int option)
254258
LDAPAPIInfo apiinfo;
255259
LDAPControl **lcs;
256260
char *strval;
261+
struct berval berbytes;
257262
#if HAVE_SASL
258263
/* unsigned long */
259264
ber_len_t blen;
@@ -406,6 +411,19 @@ LDAP_get_option(LDAPObject *self, int option)
406411
ldap_memfree(strval);
407412
return v;
408413

414+
#ifdef HAVE_TLS
415+
#ifdef LDAP_OPT_X_TLS_PEERCERT
416+
case LDAP_OPT_X_TLS_PEERCERT:
417+
#endif
418+
#endif
419+
/* Options dealing with raw data */
420+
res = LDAP_int_get_option(self, option, &berbytes);
421+
if (res != LDAP_OPT_SUCCESS)
422+
return option_error(res, "ldap_get_option");
423+
v = LDAPberval_to_object(&berbytes);
424+
ldap_memfree(berbytes.bv_val);
425+
return v;
426+
409427
case LDAP_OPT_TIMEOUT:
410428
case LDAP_OPT_NETWORK_TIMEOUT:
411429
/* Double-valued timeval options */

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
@@ -421,6 +426,36 @@ def test_multiple_starttls(self):
421426
l.simple_bind_s(self.server.root_dn, self.server.root_pw)
422427
self.assertEqual(l.whoami_s(), 'dn:' + self.server.root_dn)
423428

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

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy