From 71024518dbc9c539a5bab834080e3792ac4dbfe9 Mon Sep 17 00:00:00 2001 From: stroeder Date: Wed, 11 Sep 2013 08:16:27 +0000 Subject: [PATCH] Added support for LDAPObject.get_option(ldap.OPT_X_TLS_VERSION) --- Demo/initialize.py | 95 +++++++++++ Modules/constants.c | 378 +++++++++++++++++++++++++++++++++++++++++++ Modules/options.c | 382 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 855 insertions(+) create mode 100644 Demo/initialize.py create mode 100644 Modules/constants.c create mode 100644 Modules/options.c diff --git a/Demo/initialize.py b/Demo/initialize.py new file mode 100644 index 0000000..ab2647e --- /dev/null +++ b/Demo/initialize.py @@ -0,0 +1,95 @@ +""" +Various examples how to connect to a LDAP host with the new +factory function ldap.initialize() introduced in OpenLDAP 2 API. + +Assuming you have LDAP servers running on +ldap://localhost:1390 (LDAP with StartTLS) +ldaps://localhost:1391 (LDAP over SSL) +ldapi://%2ftmp%2fopenldap2 (domain socket /tmp/openldap2) +""" + +import sys,os,ldap + +# Switch off processing .ldaprc or ldap.conf +os.environ['LDAPNOINIT']='1' + +# Set debugging level +#ldap.set_option(ldap.OPT_DEBUG_LEVEL,255) +ldapmodule_trace_level = 1 +ldapmodule_trace_file = sys.stderr + +ldap._trace_level = ldapmodule_trace_level + +# Complete path name of the file containing all trusted CA certs +CACERTFILE='/etc/ssl/ca-bundle.pem' + +print """################################################################## +# LDAPv3 connection with StartTLS ext. op. +################################################################## +""" + +# Create LDAPObject instance +l = ldap.initialize('ldap://localhost:1390',trace_level=ldapmodule_trace_level,trace_file=ldapmodule_trace_file) + +# Set LDAP protocol version used +l.protocol_version=ldap.VERSION3 + +# Force cert validation +l.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,ldap.OPT_X_TLS_DEMAND) +# Set path name of file containing all trusted CA certificates +l.set_option(ldap.OPT_X_TLS_CACERTFILE,CACERTFILE) +# Force libldap to create a new SSL context (must be last TLS option!) +l.set_option(ldap.OPT_X_TLS_NEWCTX,0) + +# Now try StartTLS extended operation +l.start_tls_s() + +print '***ldap.OPT_X_TLS_VERSION',l.get_option(ldap.OPT_X_TLS_VERSION) +print '***ldap.OPT_X_TLS_CIPHER',l.get_option(ldap.OPT_X_TLS_CIPHER) + +# Try an explicit anon bind to provoke failure +l.simple_bind_s('','') + +# Close connection +l.unbind_s() + +print """################################################################## +# LDAPv3 connection over SSL +################################################################## +""" + +# Create LDAPObject instance +l = ldap.initialize('ldaps://localhost:1391',trace_level=ldapmodule_trace_level,trace_file=ldapmodule_trace_file) + +# Set LDAP protocol version used +l.protocol_version=ldap.VERSION3 + +# Force cert validation +l.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,ldap.OPT_X_TLS_DEMAND) +# Set path name of file containing all trusted CA certificates +l.set_option(ldap.OPT_X_TLS_CACERTFILE,CACERTFILE) +# Force libldap to create a new SSL context (must be last TLS option!) +l.set_option(ldap.OPT_X_TLS_NEWCTX,0) + +# Try an explicit anon bind to provoke failure +l.simple_bind_s('','') + +print '***ldap.OPT_X_TLS_VERSION',l.get_option(ldap.OPT_X_TLS_VERSION) +print '***ldap.OPT_X_TLS_CIPHER',l.get_option(ldap.OPT_X_TLS_CIPHER) + +# Close connection +l.unbind_s() + +print """################################################################## +# LDAPv3 connection over Unix domain socket +################################################################## +""" + +# Create LDAPObject instance +l = ldap.initialize('ldapi://%2ftmp%2fopenldap-socket',trace_level=ldapmodule_trace_level,trace_file=ldapmodule_trace_file) +# Set LDAP protocol version used +l.protocol_version=ldap.VERSION3 +# Try an explicit anon bind to provoke failure +l.simple_bind_s('','') +# Close connection +l.unbind_s() diff --git a/Modules/constants.c b/Modules/constants.c new file mode 100644 index 0000000..d815edd --- /dev/null +++ b/Modules/constants.c @@ -0,0 +1,378 @@ +/* constants defined for LDAP + * See http://www.python-ldap.org/ for details. + * $Id: constants.c,v 1.57 2013/09/11 08:16:27 stroeder Exp $ */ + +#include "common.h" +#include "constants.h" +#include "lber.h" +#include "ldap.h" + +static PyObject* reverse; +static PyObject* forward; + +/* convert an result integer into a Python string */ + +PyObject* +LDAPconstant( int val ) { + PyObject *i = PyInt_FromLong( val ); + PyObject *s = PyObject_GetItem( reverse, i ); + if (s == NULL) { + PyErr_Clear(); + return i; + } + Py_DECREF(i); + return s; +} + +/* initialise the module constants */ + +void +LDAPinit_constants( PyObject* d ) +{ + PyObject *zero, *author,*obj; + + reverse = PyDict_New(); + forward = PyDict_New(); + + PyDict_SetItemString( d, "_reverse", reverse ); + PyDict_SetItemString( d, "_forward", forward ); + +#define add_int(d, name) \ + { \ + PyObject *i = PyInt_FromLong(LDAP_##name); \ + PyDict_SetItemString( d, #name, i ); \ + Py_DECREF(i); \ + } + + /* simple constants */ + + add_int(d,API_VERSION); + add_int(d,VENDOR_VERSION); + + add_int(d,PORT); + add_int(d,VERSION1); + add_int(d,VERSION2); + add_int(d,VERSION3); + add_int(d,VERSION_MIN); + add_int(d,VERSION); + add_int(d,VERSION_MAX); + add_int(d,TAG_MESSAGE); + add_int(d,TAG_MSGID); + + add_int(d,REQ_BIND); + add_int(d,REQ_UNBIND); + add_int(d,REQ_SEARCH); + add_int(d,REQ_MODIFY); + add_int(d,REQ_ADD); + add_int(d,REQ_DELETE); + add_int(d,REQ_MODRDN); + add_int(d,REQ_COMPARE); + add_int(d,REQ_ABANDON); + + add_int(d,TAG_LDAPDN); + add_int(d,TAG_LDAPCRED); + add_int(d,TAG_CONTROLS); + add_int(d,TAG_REFERRAL); + + add_int(d,REQ_EXTENDED); +#if LDAP_API_VERSION >= 2004 + add_int(d,TAG_NEWSUPERIOR); + add_int(d,TAG_EXOP_REQ_OID); + add_int(d,TAG_EXOP_REQ_VALUE); + add_int(d,TAG_EXOP_RES_OID); + add_int(d,TAG_EXOP_RES_VALUE); +#ifdef HAVE_SASL + add_int(d,TAG_SASL_RES_CREDS); +#endif +#endif + + add_int(d,SASL_AUTOMATIC); + add_int(d,SASL_INTERACTIVE); + add_int(d,SASL_QUIET); + + /* reversibles */ + + zero = PyInt_FromLong( 0 ); + PyDict_SetItem( reverse, zero, Py_None ); + Py_DECREF( zero ); + + add_int(d,RES_BIND); + add_int(d,RES_SEARCH_ENTRY); + add_int(d,RES_SEARCH_RESULT); + add_int(d,RES_MODIFY); + add_int(d,RES_ADD); + add_int(d,RES_DELETE); + add_int(d,RES_MODRDN); + add_int(d,RES_COMPARE); + add_int(d,RES_ANY); + + add_int(d,RES_SEARCH_REFERENCE); + add_int(d,RES_EXTENDED); + add_int(d,RES_UNSOLICITED); + + add_int(d,RES_INTERMEDIATE); + + /* non-reversibles */ + + add_int(d,AUTH_NONE); + add_int(d,AUTH_SIMPLE); + add_int(d,SCOPE_BASE); + add_int(d,SCOPE_ONELEVEL); + add_int(d,SCOPE_SUBTREE); + add_int(d,MOD_ADD); + add_int(d,MOD_DELETE); + add_int(d,MOD_REPLACE); + add_int(d,MOD_INCREMENT); + add_int(d,MOD_BVALUES); + + add_int(d,MSG_ONE); + add_int(d,MSG_ALL); + add_int(d,MSG_RECEIVED); + + /* (errors.c contains the error constants) */ + + add_int(d,DEREF_NEVER); + add_int(d,DEREF_SEARCHING); + add_int(d,DEREF_FINDING); + add_int(d,DEREF_ALWAYS); + add_int(d,NO_LIMIT); + + add_int(d,OPT_API_INFO); + add_int(d,OPT_DEREF); + add_int(d,OPT_SIZELIMIT); + add_int(d,OPT_TIMELIMIT); +#ifdef LDAP_OPT_REFERRALS + add_int(d,OPT_REFERRALS); +#endif + add_int(d,OPT_ERROR_NUMBER); + add_int(d,OPT_RESTART); + add_int(d,OPT_PROTOCOL_VERSION); + add_int(d,OPT_SERVER_CONTROLS); + add_int(d,OPT_CLIENT_CONTROLS); + add_int(d,OPT_API_FEATURE_INFO); + add_int(d,OPT_HOST_NAME); + + add_int(d,OPT_DIAGNOSTIC_MESSAGE); + + add_int(d,OPT_ERROR_STRING); + add_int(d,OPT_MATCHED_DN); + add_int(d,OPT_DEBUG_LEVEL); + add_int(d,OPT_TIMEOUT); + add_int(d,OPT_REFHOPLIMIT); + add_int(d,OPT_NETWORK_TIMEOUT); + add_int(d,OPT_URI); +#ifdef LDAP_OPT_DEFBASE + add_int(d,OPT_DEFBASE); +#endif +#ifdef HAVE_TLS + add_int(d,OPT_X_TLS); +#ifdef LDAP_OPT_X_TLS_NEWCTX + add_int(d,OPT_X_TLS_CTX); +#endif + add_int(d,OPT_X_TLS_CACERTFILE); + add_int(d,OPT_X_TLS_CACERTDIR); + add_int(d,OPT_X_TLS_CERTFILE); + add_int(d,OPT_X_TLS_KEYFILE); + add_int(d,OPT_X_TLS_REQUIRE_CERT); + add_int(d,OPT_X_TLS_CIPHER_SUITE); + add_int(d,OPT_X_TLS_RANDOM_FILE); + add_int(d,OPT_X_TLS_DHFILE); + add_int(d,OPT_X_TLS_NEVER); + add_int(d,OPT_X_TLS_HARD); + add_int(d,OPT_X_TLS_DEMAND); + add_int(d,OPT_X_TLS_ALLOW); + add_int(d,OPT_X_TLS_TRY); +#ifdef LDAP_OPT_X_TLS_PEERCERT + add_int(d,OPT_X_TLS_PEERCERT); +#endif +#ifdef LDAP_OPT_X_TLS_VERSION + add_int(d,OPT_X_TLS_VERSION); +#endif +#ifdef LDAP_OPT_X_TLS_CIPHER + add_int(d,OPT_X_TLS_CIPHER); +#endif +#ifdef LDAP_OPT_X_TLS_CRLCHECK + /* only available if OpenSSL supports it => might cause backward compability problems */ + add_int(d,OPT_X_TLS_CRLCHECK); +#ifdef LDAP_OPT_X_TLS_CRLFILE + add_int(d,OPT_X_TLS_CRLFILE); +#endif + add_int(d,OPT_X_TLS_CRL_NONE); + add_int(d,OPT_X_TLS_CRL_PEER); + add_int(d,OPT_X_TLS_CRL_ALL); +#endif +#ifdef LDAP_OPT_X_TLS_NEWCTX + add_int(d,OPT_X_TLS_NEWCTX); +#endif +#ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN + add_int(d,OPT_X_TLS_PROTOCOL_MIN); +#endif +#ifdef LDAP_OPT_X_TLS_PACKAGE + add_int(d,OPT_X_TLS_PACKAGE); +#endif +#endif + add_int(d,OPT_X_SASL_MECH); + add_int(d,OPT_X_SASL_REALM); + add_int(d,OPT_X_SASL_AUTHCID); + add_int(d,OPT_X_SASL_AUTHZID); + add_int(d,OPT_X_SASL_SSF); + add_int(d,OPT_X_SASL_SSF_EXTERNAL); + add_int(d,OPT_X_SASL_SECPROPS); + add_int(d,OPT_X_SASL_SSF_MIN); + add_int(d,OPT_X_SASL_SSF_MAX); +#ifdef LDAP_OPT_X_SASL_NOCANON + add_int(d,OPT_X_SASL_NOCANON); +#endif +#ifdef LDAP_OPT_X_SASL_USERNAME + add_int(d,OPT_X_SASL_USERNAME); +#endif +#ifdef LDAP_OPT_CONNECT_ASYNC + add_int(d,OPT_CONNECT_ASYNC); +#endif +#ifdef LDAP_OPT_X_KEEPALIVE_IDLE + add_int(d,OPT_X_KEEPALIVE_IDLE); +#endif +#ifdef LDAP_OPT_X_KEEPALIVE_PROBES + add_int(d,OPT_X_KEEPALIVE_PROBES); +#endif +#ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL + add_int(d,OPT_X_KEEPALIVE_INTERVAL); +#endif + + add_int(d,DN_FORMAT_LDAP); + add_int(d,DN_FORMAT_LDAPV3); + add_int(d,DN_FORMAT_LDAPV2); + add_int(d,DN_FORMAT_DCE); + add_int(d,DN_FORMAT_UFN); + add_int(d,DN_FORMAT_AD_CANONICAL); + /* add_int(d,DN_FORMAT_LBER); */ /* "for testing only" */ + add_int(d,DN_FORMAT_MASK); + add_int(d,DN_PRETTY); + add_int(d,DN_SKIP); + add_int(d,DN_P_NOLEADTRAILSPACES); + add_int(d,DN_P_NOSPACEAFTERRDN); + add_int(d,DN_PEDANTIC); + + add_int(d,AVA_NULL); + add_int(d,AVA_STRING); + add_int(d,AVA_BINARY); + add_int(d,AVA_NONPRINTABLE); + + /*add_int(d,OPT_ON);*/ + obj = PyInt_FromLong(1); + PyDict_SetItemString( d, "OPT_ON", obj ); + Py_DECREF(obj); + /*add_int(d,OPT_OFF);*/ + obj = PyInt_FromLong(0); + PyDict_SetItemString( d, "OPT_OFF", obj ); + Py_DECREF(obj); + + add_int(d,OPT_SUCCESS); + + /* XXX - these belong in errors.c */ + + add_int(d,URL_ERR_BADSCOPE); + add_int(d,URL_ERR_MEM); + + /* author */ + + author = PyString_FromString("python-ldap Project"); + PyDict_SetItemString(d, "__author__", author); + Py_DECREF(author); + + /* add_int(d,LIBLDAP_R); */ +#ifdef HAVE_LIBLDAP_R + obj = PyInt_FromLong(1); +#else + obj = PyInt_FromLong(0); +#endif + PyDict_SetItemString( d, "LIBLDAP_R", obj ); + Py_DECREF(obj); + + /* add_int(d,SASL); */ +#ifdef HAVE_SASL + obj = PyInt_FromLong(1); +#else + obj = PyInt_FromLong(0); +#endif + PyDict_SetItemString( d, "SASL_AVAIL", obj ); + Py_DECREF(obj); + + /* add_int(d,TLS); */ +#ifdef HAVE_TLS + obj = PyInt_FromLong(1); +#else + obj = PyInt_FromLong(0); +#endif + PyDict_SetItemString( d, "TLS_AVAIL", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_MANAGEDSAIT); + PyDict_SetItemString( d, "CONTROL_MANAGEDSAIT", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_PROXY_AUTHZ); + PyDict_SetItemString( d, "CONTROL_PROXY_AUTHZ", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_SUBENTRIES); + PyDict_SetItemString( d, "CONTROL_SUBENTRIES", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_VALUESRETURNFILTER); + PyDict_SetItemString( d, "CONTROL_VALUESRETURNFILTER", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_ASSERT); + PyDict_SetItemString( d, "CONTROL_ASSERT", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_PRE_READ); + PyDict_SetItemString( d, "CONTROL_PRE_READ", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_POST_READ); + PyDict_SetItemString( d, "CONTROL_POST_READ", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_SORTREQUEST); + PyDict_SetItemString( d, "CONTROL_SORTREQUEST", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_SORTRESPONSE); + PyDict_SetItemString( d, "CONTROL_SORTRESPONSE", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_PAGEDRESULTS); + PyDict_SetItemString( d, "CONTROL_PAGEDRESULTS", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_SYNC); + PyDict_SetItemString( d, "CONTROL_SYNC", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_SYNC_STATE); + PyDict_SetItemString( d, "CONTROL_SYNC_STATE", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_SYNC_DONE); + PyDict_SetItemString( d, "CONTROL_SYNC_DONE", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_SYNC_INFO); + PyDict_SetItemString( d, "SYNC_INFO", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_PASSWORDPOLICYREQUEST); + PyDict_SetItemString( d, "CONTROL_PASSWORDPOLICYREQUEST", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_PASSWORDPOLICYRESPONSE); + PyDict_SetItemString( d, "CONTROL_PASSWORDPOLICYRESPONSE", obj ); + Py_DECREF(obj); + + obj = PyString_FromString(LDAP_CONTROL_RELAX); + PyDict_SetItemString( d, "CONTROL_RELAX", obj ); + Py_DECREF(obj); + +} diff --git a/Modules/options.c b/Modules/options.c new file mode 100644 index 0000000..9cad581 --- /dev/null +++ b/Modules/options.c @@ -0,0 +1,382 @@ +/* See http://www.python-ldap.org/ for details. + * $Id: options.c,v 1.41 2013/09/11 08:16:27 stroeder Exp $ */ + +#include "common.h" +#include "errors.h" +#include "LDAPObject.h" +#include "ldapcontrol.h" +#include "options.h" + +void +set_timeval_from_double( struct timeval *tv, double d ) { + tv->tv_usec = (long) ( fmod(d, 1.0) * 1000000.0 ); + tv->tv_sec = (long) floor(d); +} + +/** + * Converts a return code from ldap_set_option() or ldap_get_option() + * into a python error, and returns NULL. + */ +static PyObject * +option_error(int res, const char *fn) +{ + if (res == LDAP_OPT_ERROR) + PyErr_SetString(PyExc_ValueError, "option error"); + else if (res == LDAP_PARAM_ERROR) + PyErr_SetString(PyExc_ValueError, "parameter error"); + else if (res == LDAP_NO_MEMORY) + PyErr_NoMemory(); + else + PyErr_Format(PyExc_SystemError, "error %d from %s", res, fn); + return NULL; +} + +/** + * Sets an LDAP option. + * Returns 0 on failure, 1 on success + */ +int +LDAP_set_option(LDAPObject *self, int option, PyObject *value) +{ + int res; + int intval; + double doubleval; + char *strval; + struct timeval tv; + void *ptr; + LDAP *ld; + LDAPControl **controls = NULL; + + ld = self ? self->ldap : NULL; + + switch(option) { + case LDAP_OPT_API_INFO: + case LDAP_OPT_API_FEATURE_INFO: +#ifdef HAVE_SASL + case LDAP_OPT_X_SASL_SSF: +#endif + /* Read-only options */ + PyErr_SetString(PyExc_ValueError, "read-only option"); + return 0; + case LDAP_OPT_REFERRALS: + case LDAP_OPT_RESTART: +#ifdef LDAP_OPT_X_SASL_NOCANON + case LDAP_OPT_X_SASL_NOCANON: +#endif +#ifdef LDAP_OPT_CONNECT_ASYNC + case LDAP_OPT_CONNECT_ASYNC: +#endif + /* Truth-value options */ + ptr = PyObject_IsTrue(value) ? LDAP_OPT_ON : LDAP_OPT_OFF; + break; + + case LDAP_OPT_DEREF: + case LDAP_OPT_SIZELIMIT: + case LDAP_OPT_TIMELIMIT: + case LDAP_OPT_PROTOCOL_VERSION: + case LDAP_OPT_ERROR_NUMBER: + case LDAP_OPT_DEBUG_LEVEL: +#ifdef HAVE_TLS + case LDAP_OPT_X_TLS: + case LDAP_OPT_X_TLS_REQUIRE_CERT: +#ifdef LDAP_OPT_X_TLS_CRLCHECK + case LDAP_OPT_X_TLS_CRLCHECK: +#endif +#ifdef LDAP_OPT_X_TLS_NEWCTX + case LDAP_OPT_X_TLS_NEWCTX: +#endif +#ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN + case LDAP_OPT_X_TLS_PROTOCOL_MIN: +#endif +#endif +#ifdef HAVE_SASL + case LDAP_OPT_X_SASL_SSF_MIN: + case LDAP_OPT_X_SASL_SSF_MAX: +#endif +#ifdef LDAP_OPT_X_KEEPALIVE_IDLE + case LDAP_OPT_X_KEEPALIVE_IDLE: +#endif +#ifdef LDAP_OPT_X_KEEPALIVE_PROBES + case LDAP_OPT_X_KEEPALIVE_PROBES: +#endif +#ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL + case LDAP_OPT_X_KEEPALIVE_INTERVAL: +#endif + + /* integer value options */ + if (!PyArg_Parse(value, "i:set_option", &intval)) + return 0; + ptr = &intval; + break; + case LDAP_OPT_HOST_NAME: + case LDAP_OPT_URI: +#ifdef LDAP_OPT_DEFBASE + case LDAP_OPT_DEFBASE: +#endif + case LDAP_OPT_ERROR_STRING: + case LDAP_OPT_MATCHED_DN: +#ifdef HAVE_TLS + case LDAP_OPT_X_TLS_CACERTFILE: + case LDAP_OPT_X_TLS_CACERTDIR: + case LDAP_OPT_X_TLS_CERTFILE: + case LDAP_OPT_X_TLS_KEYFILE: + case LDAP_OPT_X_TLS_CIPHER_SUITE: + case LDAP_OPT_X_TLS_RANDOM_FILE: + case LDAP_OPT_X_TLS_DHFILE: +#ifdef LDAP_OPT_X_TLS_CRLFILE + case LDAP_OPT_X_TLS_CRLFILE: +#endif +#endif +#ifdef HAVE_SASL + case LDAP_OPT_X_SASL_SECPROPS: +#endif + /* String valued options */ + if (!PyArg_Parse(value, "s:set_option", &strval)) + return 0; + ptr = strval; + break; + case LDAP_OPT_TIMEOUT: + case LDAP_OPT_NETWORK_TIMEOUT: + /* Float valued timeval options */ + if (!PyArg_Parse(value, "d:set_option", &doubleval)) + return 0; + if (doubleval >= 0) { + set_timeval_from_double( &tv, doubleval ); + ptr = &tv; + } else { + ptr = NULL; + } + break; + case LDAP_OPT_SERVER_CONTROLS: + case LDAP_OPT_CLIENT_CONTROLS: + if (!LDAPControls_from_object(value, &controls)) + return 0; + ptr = controls; + break; + default: + 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 (res != LDAP_OPT_SUCCESS) { + option_error(res, "ldap_set_option"); + return 0; + } + + return 1; +} + +PyObject * +LDAP_get_option(LDAPObject *self, int option) +{ + int res; + int intval; + struct timeval *tv; + LDAPAPIInfo apiinfo; + LDAPControl **lcs; + LDAPControl *lc; + char *strval; + PyObject *extensions, *v, *tup; + Py_ssize_t i, num_extensions, num_controls; + LDAP *ld; + + ld = self ? self->ldap : NULL; + + switch(option) { + case LDAP_OPT_API_INFO: + apiinfo.ldapai_info_version = LDAP_API_INFO_VERSION; + if (self) LDAP_BEGIN_ALLOW_THREADS(self); + res = ldap_get_option( ld, option, &apiinfo ); + 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]) + num_extensions++; + extensions = PyTuple_New(num_extensions); + for (i = 0; i < num_extensions; i++) + PyTuple_SET_ITEM(extensions, i, + PyString_FromString(apiinfo.ldapai_extensions[i])); + + /* return api info as a dictionary */ + v = Py_BuildValue("{s:i, s:i, s:i, s:s, s:i, s:O}", + "info_version", apiinfo.ldapai_info_version, + "api_version", apiinfo.ldapai_api_version, + "protocol_version", apiinfo.ldapai_protocol_version, + "vendor_name", apiinfo.ldapai_vendor_name, + "vendor_version", apiinfo.ldapai_vendor_version, + "extensions", extensions); + + if (apiinfo.ldapai_vendor_name) + ldap_memfree(apiinfo.ldapai_vendor_name); + for (i = 0; i < num_extensions; i++) + ldap_memfree(apiinfo.ldapai_extensions[i]); + ldap_memfree(apiinfo.ldapai_extensions); + Py_DECREF(extensions); + + return v; + +#ifdef HAVE_SASL + case LDAP_OPT_X_SASL_SSF: +#endif + case LDAP_OPT_REFERRALS: + case LDAP_OPT_RESTART: + case LDAP_OPT_DEREF: + case LDAP_OPT_SIZELIMIT: + case LDAP_OPT_TIMELIMIT: + case LDAP_OPT_PROTOCOL_VERSION: + case LDAP_OPT_ERROR_NUMBER: + case LDAP_OPT_DEBUG_LEVEL: +#ifdef HAVE_TLS + case LDAP_OPT_X_TLS: + case LDAP_OPT_X_TLS_REQUIRE_CERT: +#ifdef LDAP_OPT_X_TLS_CRLCHECK + case LDAP_OPT_X_TLS_CRLCHECK: +#endif +#ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN + case LDAP_OPT_X_TLS_PROTOCOL_MIN: +#endif +#endif +#ifdef HAVE_SASL + case LDAP_OPT_X_SASL_SSF_MIN: + case LDAP_OPT_X_SASL_SSF_MAX: +#endif +#ifdef LDAP_OPT_X_SASL_NOCANON + case LDAP_OPT_X_SASL_NOCANON: +#endif +#ifdef LDAP_OPT_CONNECT_ASYNC + case LDAP_OPT_CONNECT_ASYNC: +#endif +#ifdef LDAP_OPT_X_KEEPALIVE_IDLE + case LDAP_OPT_X_KEEPALIVE_IDLE: +#endif +#ifdef LDAP_OPT_X_KEEPALIVE_PROBES + case LDAP_OPT_X_KEEPALIVE_PROBES: +#endif +#ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL + case LDAP_OPT_X_KEEPALIVE_INTERVAL: +#endif + /* Integer-valued options */ + if (self) LDAP_BEGIN_ALLOW_THREADS(self); + res = ldap_get_option(ld, option, &intval); + if (self) LDAP_END_ALLOW_THREADS(self); + if (res != LDAP_OPT_SUCCESS) + return option_error(res, "ldap_get_option"); + return PyInt_FromLong(intval); + + case LDAP_OPT_HOST_NAME: + case LDAP_OPT_URI: +#ifdef LDAP_OPT_DEFBASE + case LDAP_OPT_DEFBASE: +#endif + case LDAP_OPT_ERROR_STRING: + case LDAP_OPT_MATCHED_DN: +#ifdef HAVE_TLS + case LDAP_OPT_X_TLS_CACERTFILE: + case LDAP_OPT_X_TLS_CACERTDIR: + case LDAP_OPT_X_TLS_CERTFILE: + case LDAP_OPT_X_TLS_KEYFILE: + case LDAP_OPT_X_TLS_CIPHER_SUITE: + case LDAP_OPT_X_TLS_RANDOM_FILE: + case LDAP_OPT_X_TLS_DHFILE: +#ifdef LDAP_OPT_X_TLS_CRLFILE + case LDAP_OPT_X_TLS_CRLFILE: +#endif +#ifdef LDAP_OPT_X_TLS_VERSION + case LDAP_OPT_X_TLS_VERSION: +#endif +#ifdef LDAP_OPT_X_TLS_CIPHER + case LDAP_OPT_X_TLS_CIPHER: +#endif +#ifdef LDAP_OPT_X_TLS_PACKAGE + case LDAP_OPT_X_TLS_PACKAGE: +#endif +#endif +#ifdef HAVE_SASL + case LDAP_OPT_X_SASL_SECPROPS: + case LDAP_OPT_X_SASL_MECH: + case LDAP_OPT_X_SASL_REALM: + case LDAP_OPT_X_SASL_AUTHCID: + case LDAP_OPT_X_SASL_AUTHZID: +#ifdef LDAP_OPT_X_SASL_USERNAME + case LDAP_OPT_X_SASL_USERNAME: +#endif +#endif + /* String-valued options */ + if (self) LDAP_BEGIN_ALLOW_THREADS(self); + res = ldap_get_option(ld, option, &strval); + if (self) LDAP_END_ALLOW_THREADS(self); + if (res != LDAP_OPT_SUCCESS) + return option_error(res, "ldap_get_option"); + if (strval == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + v = PyString_FromString(strval); + ldap_memfree(strval); + return v; + + case LDAP_OPT_TIMEOUT: + case LDAP_OPT_NETWORK_TIMEOUT: + /* Double-valued timeval options */ + if (self) LDAP_BEGIN_ALLOW_THREADS(self); + res = ldap_get_option(ld, option, &tv); + if (self) LDAP_END_ALLOW_THREADS(self); + if (res != LDAP_OPT_SUCCESS) + return option_error(res, "ldap_get_option"); + if (tv == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + v = PyFloat_FromDouble( + (double) tv->tv_sec + ( (double) tv->tv_usec / 1000000.0 ) + ); + ldap_memfree(tv); + return v; + + case LDAP_OPT_SERVER_CONTROLS: + case LDAP_OPT_CLIENT_CONTROLS: + if (self) LDAP_BEGIN_ALLOW_THREADS(self); + res = ldap_get_option(ld, option, &lcs); + if (self) LDAP_END_ALLOW_THREADS(self); + + if (res != LDAP_OPT_SUCCESS) + return option_error(res, "ldap_get_option"); + + if (lcs == NULL) + return PyList_New(0); + + /* Get the number of controls */ + num_controls = 0; + while (lcs[num_controls]) + num_controls++; + + /* We'll build a list of controls, with each control a tuple */ + v = PyList_New(num_controls); + for (i = 0; i < num_controls; i++) { + lc = lcs[i]; + tup = Py_BuildValue("(sbs)", + lc->ldctl_oid, + lc->ldctl_iscritical, + lc->ldctl_value.bv_val); + PyList_SET_ITEM(v, i, tup); + } + + ldap_controls_free(lcs); + + return v; + + default: + PyErr_Format(PyExc_ValueError, "unknown option %d", option); + return NULL; + } +}