diff --git a/Doc/reference/ldap.rst b/Doc/reference/ldap.rst index 277520e8..fd2082fe 100644 --- a/Doc/reference/ldap.rst +++ b/Doc/reference/ldap.rst @@ -84,7 +84,13 @@ This module defines the following functions: .. py:function:: set_option(option, invalue) -> None This function sets the value of the global option specified by *option* to - *invalue*. + *invalue*. Any change to global settings + + .. note:: + + Most global settings do not affect existing :py:class:`LDAPObject` + connections. Applications should call :py:func:`set_option()` before + they establish connections with :py:func:`initialize`. .. _ldap-constants: @@ -124,10 +130,10 @@ Options :manpage:`ldap.conf(5)` and :manpage:`ldap_get_option(3)` - -For use with functions :py:func:set_option() and :py:func:get_option() -and methods :py:method:LDAPObject.set_option() and :py:method:LDAPObject.get_option() the -following option identifiers are defined as constants: +For use with functions :py:func:`set_option()` and :py:func:`get_option()` +and methods :py:meth:`LDAPObject.set_option()` and +:py:meth:`LDAPObject.get_option()` the following option identifiers +are defined as constants: .. py:data:: OPT_API_FEATURE_INFO @@ -220,34 +226,154 @@ SASL options TLS options ::::::::::: +.. warning:: + libldap does not materialize all TLS settings immediately. You must use + :py:const:`OPT_X_TLS_NEWCTX` to instruct libldap to apply pending TLS + settings and create a new internal TLS context:: + + conn = ldap.initialize(ldap_uri) + conn.set_option(ldap.OPT_X_TLS_CACERTFILE, '/path/to/ca.pem') + conn.set_option(ldap.OPT_X_TLS_NEWCTX, 0) + conn.start_tls_s() + conn.simple_bind_s(dn, password) + .. py:data:: OPT_X_TLS + .. deprecated:: 3.0 + The option is deprecated in OpenLDAP and should no longer be used. It + will be removed in the future. + +.. py:data:: OPT_X_TLS_ALL + + Value for :py:const:`OPT_X_TLS_CRLCHECK` + .. py:data:: OPT_X_TLS_ALLOW + Value for :py:const:`OPT_X_TLS_REQUIRE_CERT` + .. py:data:: OPT_X_TLS_CACERTDIR + get/set path to directory with CA certs + .. py:data:: OPT_X_TLS_CACERTFILE + get/set path to PEM file with CA certs + .. py:data:: OPT_X_TLS_CERTFILE + get/set path to file with PEM encoded cert for client cert authentication, + requires :py:const:`OPT_X_TLS_KEYFILE`. + +.. py:data:: OPT_X_TLS_CIPHER + + get cipher suite name from TLS session + .. py:data:: OPT_X_TLS_CIPHER_SUITE + get/set allowed cipher suites + +.. py:data:: OPT_X_TLS_CRLCHECK + + get/set CRL check mode. CRL validation needs :py:const:`OPT_X_TLS_CRLFILE` + + :py:const:`OPT_X_TLS_NONE` + Don't perform CRL checks + + :py:const:`OPT_X_TLS_PEER` + Perform CRL check for peer's end entity cert. + + :py:const:`OPT_X_TLS_ALL` + Perform CRL checks for the whole cert chain + +.. py:data:: OPT_X_TLS_CRLFILE + + get/set path to CRL file + .. py:data:: OPT_X_TLS_CTX + get address of internal memory address of TLS context (**DO NOT USE**) + .. py:data:: OPT_X_TLS_DEMAND + Value for :py:const:`OPT_X_TLS_REQUIRE_CERT` + .. py:data:: OPT_X_TLS_HARD + Value for :py:const:`OPT_X_TLS_REQUIRE_CERT` + .. py:data:: OPT_X_TLS_KEYFILE + get/set path to file with PEM encoded key for client cert authentication, + requires :py:const:`OPT_X_TLS_CERTFILE`. + .. py:data:: OPT_X_TLS_NEVER + Value for :py:const:`OPT_X_TLS_REQUIRE_CERT` + +.. py:data:: OPT_X_TLS_NEWCTX + + set and apply TLS settings to underlying TLS context + +.. py:data:: OPT_X_TLS_NONE + + Value for :py:const:`OPT_X_TLS_CRLCHECK` + +.. py:data:: OPT_X_TLS_PACKAGE + + Get TLS implementation, known values are + + * ``GnuTLS`` + * ``MozNSS`` (Mozilla NSS) + * ``OpenSSL`` + +.. py:data:: OPT_X_TLS_PEER + + Value for :py:const:`OPT_X_TLS_CRLCHECK` + +.. py:data:: OPT_X_TLS_PEERCERT + + Get peer's certificate as BER/DER data structure (not supported) + +.. py:data:: OPT_X_TLS_PROTOCOL_MIN + + get/set minimum protocol version (wire protocol version as int) + + * ``0x300`` for SSL 3.0 + * ``0x301`` for TLS 1.0 + * ``0x302`` for TLS 1.1 + * ``0x303`` for TLS 1.2 + * ``0x304`` for TLS 1.3 + .. py:data:: OPT_X_TLS_RANDOM_FILE + get/set path to /dev/urandom (**DO NOT USE**) + .. py:data:: OPT_X_TLS_REQUIRE_CERT + get/set validation strategy for server cert. + + :py:const:`OPT_X_TLS_NEVER` + Don't check server cert and host name + + :py:const:`OPT_X_TLS_ALLOW` + Ignore cert validation errors and don't check host name + + :py:const:`OPT_X_TLS_DEMAND` + Validate peer cert chain and host name + + :py:const:`OPT_X_TLS_HARD` + Same as :py:const:`OPT_X_TLS_DEMAND` + .. py:data:: OPT_X_TLS_TRY + .. deprecated:: 3.0 + This value is only used by slapd server internally. It will be removed + in the future. + +.. py:data:: OPT_X_TLS_VERSION + + Get negotiated TLS protocol version as string + .. _ldap-keepalive-options: Keepalive options @@ -564,6 +690,8 @@ The above exceptions are raised when a result code from an underlying API call does not indicate success. +.. _ldap-warnings: + Warnings ======== @@ -575,6 +703,16 @@ Warnings .. versionadded:: 3.0.0 +.. py:exception:: LDAPTLSWarning + + Raised when python-ldap detects missing call of + :py:meth:`LDAPObject.set_option` with + option :py:const:`OPT_X_TLS_NEWCTX`. + + See :ref:`ldap-tls-options` for details. + + .. versionadded:: 3.0.0 + .. _ldap-objects: diff --git a/Doc/spelling_wordlist.txt b/Doc/spelling_wordlist.txt index 925ddc30..6964998a 100644 --- a/Doc/spelling_wordlist.txt +++ b/Doc/spelling_wordlist.txt @@ -39,6 +39,7 @@ defresult dereferenced dereferencing desc +dev directoryOperation distinguished distributedOperation @@ -144,6 +145,7 @@ UDP Umich unparsing unsigend +urandom uri urlPrefix urlscheme diff --git a/Lib/ldap/__init__.py b/Lib/ldap/__init__.py index 3a86095f..282adf10 100644 --- a/Lib/ldap/__init__.py +++ b/Lib/ldap/__init__.py @@ -86,7 +86,7 @@ def release(self): from ldap.functions import open,initialize,init,get_option,set_option,escape_str,strf_secs,strp_secs -from ldap.ldapobject import NO_UNIQUE_ENTRY, LDAPBytesWarning +from ldap.ldapobject import NO_UNIQUE_ENTRY, LDAPBytesWarning, LDAPTLSWarning from ldap.dn import explode_dn,explode_rdn,str2dn,dn2str del str2dn diff --git a/Lib/ldap/constants.py b/Lib/ldap/constants.py index 4c109ac4..1f03407e 100644 --- a/Lib/ldap/constants.py +++ b/Lib/ldap/constants.py @@ -281,7 +281,6 @@ class Str(Constant): TLSInt('OPT_X_TLS_DEMAND'), TLSInt('OPT_X_TLS_ALLOW'), TLSInt('OPT_X_TLS_TRY'), - TLSInt('OPT_X_TLS_PEERCERT', optional=True), TLSInt('OPT_X_TLS_VERSION', optional=True), TLSInt('OPT_X_TLS_CIPHER', optional=True), diff --git a/Lib/ldap/ldapobject.py b/Lib/ldap/ldapobject.py index f37ef24a..6b9da293 100644 --- a/Lib/ldap/ldapobject.py +++ b/Lib/ldap/ldapobject.py @@ -14,7 +14,8 @@ 'LDAPObject', 'SimpleLDAPObject', 'ReconnectLDAPObject', - 'LDAPBytesWarning' + 'LDAPBytesWarning', + 'LDAPTLSWarning', ] @@ -25,6 +26,7 @@ import sys,time,pprint,_ldap,ldap,ldap.sasl,ldap.functions import warnings +from _ldap import LDAPTLSWarning from ldap.schema import SCHEMA_ATTRS from ldap.controls import LDAPControl,DecodeControlTuples,RequestControlTuples from ldap.extop import ExtendedRequest,ExtendedResponse diff --git a/Makefile b/Makefile index e4ff75ac..edf97774 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,10 @@ PYTHON_SUPP=/usr/share/doc/python3-devel/valgrind-python.supp .NOTPARALLEL: .PHONY: all -all: +all: Modules/constants_generated.h + +Modules/constants_generated.h: Lib/ldap/constants.py + $(PYTHON) $^ > $@ .PHONY: clean clean: diff --git a/Modules/LDAPObject.c b/Modules/LDAPObject.c index addbe961..ba352b40 100644 --- a/Modules/LDAPObject.c +++ b/Modules/LDAPObject.c @@ -29,6 +29,7 @@ newLDAPObject( LDAP* l ) self->ldap = l; self->_save = NULL; self->valid = 1; + self->need_tls_newctx = 0; return self; } @@ -54,18 +55,42 @@ dealloc( LDAPObject* self ) */ /* - * check to see if the LDAPObject is valid, + * check to see if the LDAPObject is valid and TLS settings are applied, * ie has been opened, and not closed. An exception is set if not valid. */ static int -not_valid( LDAPObject* l ) { - if (l->valid) { - return 0; - } else { +not_valid( LDAPObject* l, int check_tls ) { + if (!l->valid) { PyErr_SetString( LDAPexception_class, "LDAP connection invalid" ); return 1; } + if (l->need_tls_newctx) { + if (PyErr_WarnEx(LDAPTLSWarning_class, + "An OPT_X_TLS option was set but not applied. " + "You must call set_opt(ldap.OPT_X_TLS_NEWCTX, 0) " + "on the connection to apply new settings!", + 0) == -1) { + return 1; + } +#if 0 + else { + /* Apply setting and reset warning if warning was not fatal. */ + int res; + PyObject *intval = PyInt_FromLong(0); + + if (intval == NULL) { + return 1; + } + res = LDAP_set_option(l, LDAP_OPT_X_TLS_NEWCTX, intval); + Py_DECREF(intval); + if (res != 1) { + return 1; + } + } +#endif + } + return 0; } /* free a LDAPMod (complete or partially) allocated in Tuple_to_LDAPMod() */ @@ -362,7 +387,7 @@ l_ldap_unbind_ext( LDAPObject* self, PyObject* args ) int ldaperror; if (!PyArg_ParseTuple( args, "|OO", &serverctrls, &clientctrls)) return NULL; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) @@ -405,7 +430,7 @@ l_ldap_abandon_ext( LDAPObject* self, PyObject* args ) int ldaperror; if (!PyArg_ParseTuple( args, "i|OO", &msgid, &serverctrls, &clientctrls)) return NULL; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) @@ -450,7 +475,7 @@ l_ldap_add_ext( LDAPObject* self, PyObject *args ) LDAPMod **mods; if (!PyArg_ParseTuple( args, "sO|OO", &dn, &modlist, &serverctrls, &clientctrls )) return NULL; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; mods = List_to_LDAPMods( modlist, 1 ); if (mods == NULL) @@ -502,7 +527,7 @@ l_ldap_simple_bind( LDAPObject* self, PyObject* args ) if (!PyArg_ParseTuple( args, "ss#|OO", &who, &cred.bv_val, &cred_len, &serverctrls, &clientctrls )) return NULL; cred.bv_len = (ber_len_t) cred_len; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) @@ -652,7 +677,7 @@ l_ldap_sasl_bind_s( LDAPObject* self, PyObject* args ) if (!PyArg_ParseTuple(args, "zzz#OO", &dn, &mechanism, &cred.bv_val, &cred_len, &serverctrls, &clientctrls )) return NULL; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; cred.bv_len = cred_len; @@ -719,7 +744,7 @@ l_ldap_sasl_interactive_bind_s( LDAPObject* self, PyObject* args ) #endif return NULL; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) @@ -781,7 +806,7 @@ l_ldap_cancel( LDAPObject* self, PyObject* args ) int ldaperror; if (!PyArg_ParseTuple( args, "i|OO", &cancelid, &serverctrls, &clientctrls)) return NULL; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) @@ -829,7 +854,7 @@ l_ldap_compare_ext( LDAPObject* self, PyObject *args ) if (!PyArg_ParseTuple( args, "sss#|OO", &dn, &attr, &value.bv_val, &value_len, &serverctrls, &clientctrls )) return NULL; value.bv_len = (ber_len_t) value_len; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) @@ -872,7 +897,7 @@ l_ldap_delete_ext( LDAPObject* self, PyObject *args ) int ldaperror; if (!PyArg_ParseTuple( args, "s|OO", &dn, &serverctrls, &clientctrls )) return NULL; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) @@ -917,7 +942,7 @@ l_ldap_modify_ext( LDAPObject* self, PyObject *args ) LDAPMod **mods; if (!PyArg_ParseTuple( args, "sO|OO", &dn, &modlist, &serverctrls, &clientctrls )) return NULL; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; mods = List_to_LDAPMods( modlist, 0 ); if (mods == NULL) @@ -971,7 +996,7 @@ l_ldap_rename( LDAPObject* self, PyObject *args ) if (!PyArg_ParseTuple( args, "ss|ziOO", &dn, &newrdn, &newSuperior, &delold, &serverctrls, &clientctrls )) return NULL; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) @@ -1024,7 +1049,7 @@ l_ldap_result4( LDAPObject* self, PyObject *args ) if (!PyArg_ParseTuple( args, "|iidiii", &msgid, &all, &timeout, &add_ctrls, &add_intermediates, &add_extop )) return NULL; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; if (timeout >= 0) { tvp = &tv; @@ -1165,7 +1190,7 @@ l_ldap_search_ext( LDAPObject* self, PyObject* args ) if (!PyArg_ParseTuple( args, "sis|OiOOdi", &base, &scope, &filter, &attrlist, &attrsonly, &serverctrls, &clientctrls, &timeout, &sizelimit )) return NULL; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; if (!attrs_from_List( attrlist, &attrs )) return NULL; @@ -1225,7 +1250,7 @@ l_ldap_whoami_s( LDAPObject* self, PyObject* args ) int ldaperror; if (!PyArg_ParseTuple( args, "|OO", &serverctrls, &clientctrls)) return NULL; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) @@ -1266,7 +1291,7 @@ l_ldap_start_tls_s( LDAPObject* self, PyObject* args ) int ldaperror; if (!PyArg_ParseTuple( args, "" )) return NULL; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; LDAP_BEGIN_ALLOW_THREADS( self ); ldaperror = ldap_start_tls_s( self->ldap, NULL, NULL ); @@ -1292,6 +1317,7 @@ l_ldap_set_option(PyObject* self, PyObject *args) if (!PyArg_ParseTuple(args, "iO:set_option", &option, &value)) return NULL; + /* not_valid(self, 0) */ if (!LDAP_set_option((LDAPObject *)self, option, value)) return NULL; Py_INCREF(Py_None); @@ -1305,7 +1331,7 @@ static PyObject* l_ldap_get_option(PyObject* self, PyObject *args) { int option; - + /* not_valid(self, 0) */ if (!PyArg_ParseTuple(args, "i:get_option", &option)) return NULL; return LDAP_get_option((LDAPObject *)self, option); @@ -1338,7 +1364,7 @@ l_ldap_passwd( LDAPObject* self, PyObject *args ) oldpw.bv_len = (ber_len_t) oldpw_len; newpw.bv_len = (ber_len_t) newpw_len; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) @@ -1390,7 +1416,7 @@ l_ldap_extended_operation( LDAPObject* self, PyObject *args ) if (!PyArg_ParseTuple( args, "sz#|OO", &reqoid, &reqvalue.bv_val, &reqvalue.bv_len, &serverctrls, &clientctrls )) return NULL; - if (not_valid(self)) return NULL; + if (not_valid(self, 1)) return NULL; if (!PyNone_Check(serverctrls)) { if (!LDAPControls_from_object(serverctrls, &server_ldcs)) diff --git a/Modules/LDAPObject.h b/Modules/LDAPObject.h index 8cd6fc3e..5f92af3c 100644 --- a/Modules/LDAPObject.h +++ b/Modules/LDAPObject.h @@ -22,6 +22,7 @@ typedef struct { LDAP* ldap; _threadstate _save; /* for thread saving on referrals */ int valid; + int need_tls_newctx; } LDAPObject; extern PyTypeObject LDAP_Type; diff --git a/Modules/constants.c b/Modules/constants.c index c2b595c1..f42d5661 100644 --- a/Modules/constants.c +++ b/Modules/constants.c @@ -11,6 +11,10 @@ PyObject* LDAPexception_class; +/* warnings */ + +PyObject *LDAPTLSWarning_class; + /* list of exception classes */ #define LDAP_ERROR_MIN LDAP_REFERRAL_LIMIT_EXCEEDED @@ -150,6 +154,18 @@ LDAPinit_constants( PyObject* m ) if (PyModule_AddObject(m, "error", LDAPexception_class) != 0) return -1; Py_INCREF(LDAPexception_class); + /* TLS Warning */ + LDAPTLSWarning_class = PyErr_NewException("ldap.LDAPTLSWarning", + PyExc_RuntimeWarning, + NULL); + if (LDAPTLSWarning_class == NULL) { + return -1; + } + if (PyModule_AddObject(m, "LDAPTLSWarning", LDAPTLSWarning_class) != 0) { + return -1; + } + Py_INCREF(LDAPTLSWarning_class); + /* Generated constants -- see Lib/ldap/constants.py */ #define add_err(n) do { \ diff --git a/Modules/constants.h b/Modules/constants.h index 4056f907..f68af13e 100644 --- a/Modules/constants.h +++ b/Modules/constants.h @@ -11,6 +11,7 @@ extern int LDAPinit_constants( PyObject* m ); extern PyObject* LDAPconstant( int ); extern PyObject* LDAPexception_class; +extern PyObject* LDAPTLSWarning_class; extern PyObject* LDAPerror( LDAP*, char*msg ); PyObject* LDAPerr(int errnum); diff --git a/Modules/constants_generated.h b/Modules/constants_generated.h index 083ba161..78b68760 100644 --- a/Modules/constants_generated.h +++ b/Modules/constants_generated.h @@ -216,11 +216,6 @@ add_int(OPT_X_TLS_DEMAND); add_int(OPT_X_TLS_ALLOW); add_int(OPT_X_TLS_TRY); -#if defined(LDAP_OPT_X_TLS_PEERCERT) -add_int(OPT_X_TLS_PEERCERT); -#endif - - #if defined(LDAP_OPT_X_TLS_VERSION) add_int(OPT_X_TLS_VERSION); #endif diff --git a/Modules/options.c b/Modules/options.c index ee606d46..114da387 100644 --- a/Modules/options.c +++ b/Modules/options.c @@ -23,7 +23,7 @@ option_error(int res, const char *fn) PyErr_SetString(PyExc_ValueError, "option error"); else if (res == LDAP_PARAM_ERROR) PyErr_SetString(PyExc_ValueError, "parameter error"); - else if (res == LDAP_NO_MEMORY) + else if (res == LDAP_NO_MEMORY) PyErr_NoMemory(); else PyErr_Format(PyExc_SystemError, "error %d from %s", res, fn); @@ -184,14 +184,47 @@ LDAP_set_option(LDAPObject *self, int option, PyObject *value) PyErr_Format(PyExc_ValueError, "unknown option %d", option); return 0; } - + if (self) LDAP_BEGIN_ALLOW_THREADS(self); res = ldap_set_option(ld, option, ptr); if (self) LDAP_END_ALLOW_THREADS(self); if ((option == LDAP_OPT_SERVER_CONTROLS) || (option == LDAP_OPT_CLIENT_CONTROLS)) LDAPControl_List_DEL(controls); - + +#if defined(HAVE_TLS) && defined(LDAP_OPT_X_TLS_NEWCTX) + /* set needs_tls_newctx for any OPT_X_TLS option */ + if (self != NULL) { + switch(option) { + case LDAP_OPT_X_TLS_REQUIRE_CERT: /* fallthrough */ +#ifdef LDAP_OPT_X_TLS_CRLCHECK + case LDAP_OPT_X_TLS_CRLCHECK: /* fallthrough */ +#endif +#ifdef LDAP_OPT_X_TLS_CRLFILE + case LDAP_OPT_X_TLS_CRLFILE: /* fallthrough */ +#endif +#ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN + case LDAP_OPT_X_TLS_PROTOCOL_MIN: /* fallthrough */ +#endif + case LDAP_OPT_X_TLS_CACERTFILE: /* fallthrough */ + case LDAP_OPT_X_TLS_CACERTDIR: /* fallthrough */ + case LDAP_OPT_X_TLS_CERTFILE: /* fallthrough */ + case LDAP_OPT_X_TLS_KEYFILE: /* fallthrough */ + case LDAP_OPT_X_TLS_CIPHER_SUITE: /* fallthrough */ + case LDAP_OPT_X_TLS_RANDOM_FILE: /* fallthrough */ + case LDAP_OPT_X_TLS_DHFILE: /* fallthrough */ + self->need_tls_newctx = 1; + break; + case LDAP_OPT_X_TLS_NEWCTX: + self->need_tls_newctx = 0; + break; + default: + break; + } + } + #endif /* HAVE_TLS */ + + if (res != LDAP_OPT_SUCCESS) { option_error(res, "ldap_set_option"); return 0; @@ -223,7 +256,7 @@ LDAP_get_option(LDAPObject *self, int option) if (self) LDAP_END_ALLOW_THREADS(self); if (res != LDAP_OPT_SUCCESS) return option_error(res, "ldap_get_option"); - + /* put the extensions into tuple form */ num_extensions = 0; while (apiinfo.ldapai_extensions[num_extensions]) diff --git a/Modules/options.h b/Modules/options.h index 570fdc15..99096f6d 100644 --- a/Modules/options.h +++ b/Modules/options.h @@ -5,3 +5,5 @@ int LDAP_set_option(LDAPObject *self, int option, PyObject *value); PyObject *LDAP_get_option(LDAPObject *self, int option); void set_timeval_from_double( struct timeval *tv, double d ); + + diff --git a/Tests/t_cext.py b/Tests/t_cext.py index d8233dce..5b7b7740 100644 --- a/Tests/t_cext.py +++ b/Tests/t_cext.py @@ -8,7 +8,9 @@ from __future__ import unicode_literals import os +import sys import unittest +import warnings from slapdtest import SlapdTestCase, requires_tls @@ -817,6 +819,18 @@ def test_invalid_controls(self): l.sasl_interactive_bind_s, 'who', 'SASLObject', post=(1,)) self.assertInvalidControls(l.unbind_ext) + def assertTLSError(self, exc): + self.assertIsInstance(exc, _ldap.CONNECT_ERROR) + # known resaons: + # Ubuntu on Travis: '(unknown error code)' + # OpenSSL 1.1: error:1416F086:SSL routines:\ + # tls_process_server_certificate:certificate verify failed + # NSS: TLS error -8172:Peer's certificate issuer has \ + # been marked as not trusted by the user. + candidates = ('certificate', 'tls', '(unknown error code)') + if not any(s in str(exc).lower() for s in candidates): + self.fail(exc) + @requires_tls() def test_tls_ext(self): l = self._open_conn(bind=False) @@ -833,16 +847,7 @@ def test_tls_ext_noca(self): l.set_option(_ldap.OPT_PROTOCOL_VERSION, _ldap.VERSION3) with self.assertRaises(_ldap.CONNECT_ERROR) as e: l.start_tls_s() - # known resaons: - # Ubuntu on Travis: '(unknown error code)' - # OpenSSL 1.1: error:1416F086:SSL routines:\ - # tls_process_server_certificate:certificate verify failed - # NSS: TLS error -8172:Peer's certificate issuer has \ - # been marked as not trusted by the user. - msg = str(e.exception) - candidates = ('certificate', 'tls', '(unknown error code)') - if not any(s in msg.lower() for s in candidates): - self.fail(msg) + self.assertTLSError(e.exception) @requires_tls() def test_tls_ext_clientcert(self): @@ -853,7 +858,35 @@ def test_tls_ext_clientcert(self): l.set_option(_ldap.OPT_X_TLS_KEYFILE, self.server.clientkey) l.set_option(_ldap.OPT_X_TLS_REQUIRE_CERT, _ldap.OPT_X_TLS_HARD) l.set_option(_ldap.OPT_X_TLS_NEWCTX, 0) - l.start_tls_s() + with warnings.catch_warnings(record=True) as w: + warnings.resetwarnings() + warnings.simplefilter('once', _ldap.LDAPTLSWarning) + l.start_tls_s() + # No warning for OPT_X_TLS_NEWCTX + self.assertEqual(len(w), 0) + + @requires_tls() + def test_tls_warn_newctx(self): + l = self._open_conn(bind=False) + l.set_option(_ldap.OPT_PROTOCOL_VERSION, _ldap.VERSION3) + # don't check cert chain + l.set_option(_ldap.OPT_X_TLS_REQUIRE_CERT, _ldap.OPT_X_TLS_NEVER) + l.set_option(_ldap.OPT_X_TLS_NEWCTX, 0) + # load CA cert but don't call NEWCTX + l.set_option(_ldap.OPT_X_TLS_CACERTFILE, self.server.cafile) + with warnings.catch_warnings(record=True) as w: + warnings.resetwarnings() + warnings.simplefilter('always', _ldap.LDAPTLSWarning) + # This emits one warning. + l.start_tls_s() + self.assertEqual(len(w), 1) + self.assertEqual(w[0].category, _ldap.LDAPTLSWarning) + self.assertEqual( + str(w[0].message), + "An OPT_X_TLS option was set but not applied. You must call " + "set_opt(ldap.OPT_X_TLS_NEWCTX, 0) on the connection to apply " + "new settings!" + ) @requires_tls() def test_tls_packages(self): diff --git a/Tests/t_ldapobject.py b/Tests/t_ldapobject.py index 835512b0..ce691a51 100644 --- a/Tests/t_ldapobject.py +++ b/Tests/t_ldapobject.py @@ -350,6 +350,9 @@ def test_ldapbyteswarning(self): "LDAP connection" % self.server.suffix ) + def test_ldaptlswarning(self): + self.assertIsSubclass(ldap.LDAPBytesWarning, Warning) + class Test01_ReconnectLDAPObject(Test00_SimpleLDAPObject): """ 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