summaryrefslogtreecommitdiffstats
path: root/ldap/c-sdk/libraries/libldap/saslbind.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/c-sdk/libraries/libldap/saslbind.c')
-rw-r--r--ldap/c-sdk/libraries/libldap/saslbind.c877
1 files changed, 877 insertions, 0 deletions
diff --git a/ldap/c-sdk/libraries/libldap/saslbind.c b/ldap/c-sdk/libraries/libldap/saslbind.c
new file mode 100644
index 000000000..5cbe73bbf
--- /dev/null
+++ b/ldap/c-sdk/libraries/libldap/saslbind.c
@@ -0,0 +1,877 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+#include "ldap-int.h"
+
+#ifdef LDAP_SASLIO_HOOKS
+/*
+ * Global SASL Init data
+ */
+
+static int
+nsldapi_sasl_fail()
+{
+ return( SASL_FAIL );
+}
+
+sasl_callback_t client_callbacks[] = {
+ { SASL_CB_GETOPT, nsldapi_sasl_fail, NULL },
+ { SASL_CB_GETREALM, NULL, NULL },
+ { SASL_CB_USER, NULL, NULL },
+ { SASL_CB_CANON_USER, NULL, NULL },
+ { SASL_CB_AUTHNAME, NULL, NULL },
+ { SASL_CB_PASS, NULL, NULL },
+ { SASL_CB_ECHOPROMPT, NULL, NULL },
+ { SASL_CB_NOECHOPROMPT, NULL, NULL },
+ { SASL_CB_LIST_END, NULL, NULL }
+};
+
+int
+nsldapi_sasl_cvterrno( LDAP *ld, int err, char *msg )
+{
+ int rc = LDAP_LOCAL_ERROR;
+
+ switch (err) {
+ case SASL_OK:
+ rc = LDAP_SUCCESS;
+ break;
+ case SASL_NOMECH:
+ rc = LDAP_AUTH_UNKNOWN;
+ break;
+ case SASL_BADSERV:
+ rc = LDAP_CONNECT_ERROR;
+ break;
+ case SASL_DISABLED:
+ case SASL_ENCRYPT:
+ case SASL_EXPIRED:
+ case SASL_NOUSERPASS:
+ case SASL_NOVERIFY:
+ case SASL_PWLOCK:
+ case SASL_TOOWEAK:
+ case SASL_UNAVAIL:
+ case SASL_WEAKPASS:
+ rc = LDAP_INAPPROPRIATE_AUTH;
+ break;
+ case SASL_BADAUTH:
+ case SASL_NOAUTHZ:
+ rc = LDAP_INVALID_CREDENTIALS;
+ break;
+ case SASL_NOMEM:
+ rc = LDAP_NO_MEMORY;
+ break;
+ case SASL_NOUSER:
+ rc = LDAP_NO_SUCH_OBJECT;
+ break;
+ case SASL_CONTINUE:
+ case SASL_FAIL:
+ case SASL_INTERACT:
+ default:
+ rc = LDAP_LOCAL_ERROR;
+ break;
+ }
+
+ LDAP_SET_LDERRNO( ld, rc, NULL, msg );
+ return( rc );
+}
+
+#ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
+/*
+ * Get available SASL Mechanisms supported by the server
+ */
+
+static int
+nsldapi_get_sasl_mechs ( LDAP *ld, char **pmech )
+{
+ char *attr[] = { "supportedSASLMechanisms", NULL };
+ char **values, **v, *mech, *m;
+ LDAPMessage *res, *e;
+ struct timeval timeout;
+ int slen, rc;
+
+ if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
+ return( LDAP_PARAM_ERROR );
+ }
+
+ timeout.tv_sec = SEARCH_TIMEOUT_SECS;
+ timeout.tv_usec = 0;
+
+ rc = ldap_search_st( ld, "", LDAP_SCOPE_BASE,
+ "objectclass=*", attr, 0, &timeout, &res );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
+ }
+
+ e = ldap_first_entry( ld, res );
+ if ( e == NULL ) {
+ ldap_msgfree( res );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_OBJECT, NULL, NULL );
+ }
+ return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
+ }
+
+ values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
+ if ( values == NULL ) {
+ ldap_msgfree( res );
+ LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_ATTRIBUTE, NULL, NULL );
+ return( LDAP_NO_SUCH_ATTRIBUTE );
+ }
+
+ slen = 0;
+ for(v = values; *v != NULL; v++ ) {
+ slen += strlen(*v) + 1;
+ }
+ if ( (mech = NSLDAPI_CALLOC(1, slen)) == NULL) {
+ ldap_value_free( values );
+ ldap_msgfree( res );
+ LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
+ return( LDAP_NO_MEMORY );
+ }
+ m = mech;
+ for(v = values; *v; v++) {
+ if (v != values) {
+ *m++ = ' ';
+ }
+ slen = strlen(*v);
+ strncpy(m, *v, slen);
+ m += slen;
+ }
+ *m = '\0';
+
+ ldap_value_free( values );
+ ldap_msgfree( res );
+
+ *pmech = mech;
+
+ return( LDAP_SUCCESS );
+}
+#endif /* LDAP_SASLIO_GET_MECHS_FROM_SERVER */
+
+int
+nsldapi_sasl_secprops(
+ const char *in,
+ sasl_security_properties_t *secprops )
+{
+ int i;
+ char **props = NULL;
+ char *inp;
+ unsigned sflags = 0;
+ sasl_ssf_t max_ssf = 0;
+ sasl_ssf_t min_ssf = 0;
+ unsigned maxbufsize = 0;
+ int got_sflags = 0;
+ int got_max_ssf = 0;
+ int got_min_ssf = 0;
+ int got_maxbufsize = 0;
+
+ if (in == NULL) {
+ return LDAP_PARAM_ERROR;
+ }
+ inp = nsldapi_strdup(in);
+ if (inp == NULL) {
+ return LDAP_PARAM_ERROR;
+ }
+ props = ldap_str2charray( inp, "," );
+ NSLDAPI_FREE( inp );
+
+ if( props == NULL || secprops == NULL ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ for( i=0; props[i]; i++ ) {
+ if( strcasecmp(props[i], "none") == 0 ) {
+ got_sflags++;
+
+ } else if( strcasecmp(props[i], "noactive") == 0 ) {
+ got_sflags++;
+ sflags |= SASL_SEC_NOACTIVE;
+
+ } else if( strcasecmp(props[i], "noanonymous") == 0 ) {
+ got_sflags++;
+ sflags |= SASL_SEC_NOANONYMOUS;
+
+ } else if( strcasecmp(props[i], "nodict") == 0 ) {
+ got_sflags++;
+ sflags |= SASL_SEC_NODICTIONARY;
+
+ } else if( strcasecmp(props[i], "noplain") == 0 ) {
+ got_sflags++;
+ sflags |= SASL_SEC_NOPLAINTEXT;
+
+ } else if( strcasecmp(props[i], "forwardsec") == 0 ) {
+ got_sflags++;
+ sflags |= SASL_SEC_FORWARD_SECRECY;
+
+ } else if( strcasecmp(props[i], "passcred") == 0 ) {
+ got_sflags++;
+ sflags |= SASL_SEC_PASS_CREDENTIALS;
+
+ } else if( strncasecmp(props[i],
+ "minssf=", sizeof("minssf")) == 0 ) {
+ if( isdigit( props[i][sizeof("minssf")] ) ) {
+ got_min_ssf++;
+ min_ssf = atoi( &props[i][sizeof("minssf")] );
+ } else {
+ return LDAP_NOT_SUPPORTED;
+ }
+
+ } else if( strncasecmp(props[i],
+ "maxssf=", sizeof("maxssf")) == 0 ) {
+ if( isdigit( props[i][sizeof("maxssf")] ) ) {
+ got_max_ssf++;
+ max_ssf = atoi( &props[i][sizeof("maxssf")] );
+ } else {
+ return LDAP_NOT_SUPPORTED;
+ }
+
+ } else if( strncasecmp(props[i],
+ "maxbufsize=", sizeof("maxbufsize")) == 0 ) {
+ if( isdigit( props[i][sizeof("maxbufsize")] ) ) {
+ got_maxbufsize++;
+ maxbufsize = atoi( &props[i][sizeof("maxbufsize")] );
+ if( maxbufsize &&
+ (( maxbufsize < SASL_MIN_BUFF_SIZE )
+ || (maxbufsize > SASL_MAX_BUFF_SIZE ))) {
+ return( LDAP_PARAM_ERROR );
+ }
+ } else {
+ return( LDAP_NOT_SUPPORTED );
+ }
+ } else {
+ return( LDAP_NOT_SUPPORTED );
+ }
+ }
+
+ if(got_sflags) {
+ secprops->security_flags = sflags;
+ }
+ if(got_min_ssf) {
+ secprops->min_ssf = min_ssf;
+ }
+ if(got_max_ssf) {
+ secprops->max_ssf = max_ssf;
+ }
+ if(got_maxbufsize) {
+ secprops->maxbufsize = maxbufsize;
+ }
+
+ ldap_charray_free( props );
+ return( LDAP_SUCCESS );
+}
+#endif /* LDAP_SASLIO_HOOKS */
+
+static int
+nsldapi_sasl_bind_s(
+ LDAP *ld,
+ const char *dn,
+ const char *mechanism,
+ const struct berval *cred,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ struct berval **servercredp,
+ LDAPControl ***responsectrls
+)
+{
+ int err, msgid;
+ LDAPMessage *result;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_sasl_bind_s\n", 0, 0, 0 );
+
+ if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
+ return( LDAP_PARAM_ERROR );
+ }
+
+ if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) {
+ LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
+ return( LDAP_NOT_SUPPORTED );
+ }
+
+ if ( ( err = ldap_sasl_bind( ld, dn, mechanism, cred, serverctrls,
+ clientctrls, &msgid )) != LDAP_SUCCESS )
+ return( err );
+
+ if ( ldap_result( ld, msgid, 1, (struct timeval *) 0, &result ) == -1 )
+ return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
+
+ /* Get the controls sent by the server if requested */
+ if ( responsectrls ) {
+ if ( ( err = ldap_parse_result( ld, result, &err, NULL, NULL,
+ NULL, responsectrls, 0 )) != LDAP_SUCCESS )
+ return( err );
+ }
+
+ err = ldap_parse_sasl_bind_result( ld, result, servercredp, 0 );
+ if (err != LDAP_SUCCESS && err != LDAP_SASL_BIND_IN_PROGRESS) {
+ ldap_msgfree( result );
+ return( err );
+ }
+
+ return( ldap_result2error( ld, result, 1 ) );
+}
+
+#ifdef LDAP_SASLIO_HOOKS
+static int
+nsldapi_sasl_do_bind( LDAP *ld, const char *dn,
+ const char *mechs, unsigned flags,
+ LDAP_SASL_INTERACT_PROC *callback, void *defaults,
+ LDAPControl **sctrl, LDAPControl **cctrl, LDAPControl ***rctrl )
+{
+ sasl_interact_t *prompts = NULL;
+ sasl_conn_t *ctx = NULL;
+ sasl_ssf_t *ssf = NULL;
+ const char *mech = NULL;
+ int saslrc, rc;
+ struct berval ccred;
+ unsigned credlen;
+ int stepnum = 1;
+ char *sasl_username = NULL;
+
+ if (rctrl) {
+ /* init to NULL so we can call ldap_controls_free below */
+ *rctrl = NULL;
+ }
+
+ if (NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3) {
+ LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
+ return( LDAP_NOT_SUPPORTED );
+ }
+
+ /* shouldn't happen */
+ if (callback == NULL) {
+ return( LDAP_LOCAL_ERROR );
+ }
+
+ if ( (rc = nsldapi_sasl_open(ld, NULL, &ctx, 0)) != LDAP_SUCCESS ) {
+ return( rc );
+ }
+
+ ccred.bv_val = NULL;
+ ccred.bv_len = 0;
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "Starting SASL/%s authentication\n",
+ (mechs ? mechs : ""), 0, 0 );
+
+ do {
+ saslrc = sasl_client_start( ctx,
+ mechs,
+ &prompts,
+ (const char **)&ccred.bv_val,
+ &credlen,
+ &mech );
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of client start for SASL/%s authentication\n",
+ stepnum, (mech ? mech : ""), 0 );
+ stepnum++;
+
+ if( saslrc == SASL_INTERACT &&
+ (callback)(ld, flags, defaults, prompts) != LDAP_SUCCESS ) {
+ break;
+ }
+ } while ( saslrc == SASL_INTERACT );
+
+ ccred.bv_len = credlen;
+
+ if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
+ return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
+ }
+
+ stepnum = 1;
+
+ do {
+ struct berval *scred;
+ int clientstepnum = 1;
+
+ scred = NULL;
+
+ if (rctrl) {
+ /* if we're looping again, we need to free any controls set
+ during the previous loop */
+ /* NOTE that this assumes we only care about the controls
+ returned by the last call to nsldapi_sasl_bind_s - if
+ we care about _all_ controls, we will have to figure out
+ some way to append them each loop go round */
+ ldap_controls_free(*rctrl);
+ *rctrl = NULL;
+ }
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of bind for SASL/%s authentication\n",
+ stepnum, (mech ? mech : ""), 0 );
+ stepnum++;
+
+ /* notify server of a sasl bind step */
+ rc = nsldapi_sasl_bind_s(ld, dn, mech, &ccred,
+ sctrl, cctrl, &scred, rctrl);
+
+ if ( ccred.bv_val != NULL ) {
+ ccred.bv_val = NULL;
+ }
+
+ if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
+ ber_bvfree( scred );
+ return( rc );
+ }
+
+ if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) {
+ /* we're done, no need to step */
+ if( scred ) {
+ if ( scred->bv_len == 0 ) { /* MS AD sends back empty screds */
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "SASL BIND complete - ignoring empty credential response\n",
+ 0, 0, 0);
+ ber_bvfree( scred );
+ } else {
+ /* but server provided us with data! */
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "SASL BIND complete but invalid because server responded with credentials - length [%u]\n",
+ scred->bv_len, 0, 0);
+ ber_bvfree( scred );
+ LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR,
+ NULL, "Error during SASL handshake - invalid server credential response" );
+ return( LDAP_LOCAL_ERROR );
+ }
+ }
+ break;
+ }
+
+ /* perform the next step of the sasl bind */
+ do {
+ LDAPDebug(LDAP_DEBUG_TRACE, "Doing client step %d of bind step %d for SASL/%s authentication\n",
+ clientstepnum, stepnum, (mech ? mech : "") );
+ clientstepnum++;
+ saslrc = sasl_client_step( ctx,
+ (scred == NULL) ? NULL : scred->bv_val,
+ (scred == NULL) ? 0 : scred->bv_len,
+ &prompts,
+ (const char **)&ccred.bv_val,
+ &credlen );
+
+ if( saslrc == SASL_INTERACT &&
+ (callback)(ld, flags, defaults, prompts)
+ != LDAP_SUCCESS ) {
+ break;
+ }
+ } while ( saslrc == SASL_INTERACT );
+
+ ccred.bv_len = credlen;
+ ber_bvfree( scred );
+
+ if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
+ return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
+ }
+ } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+
+ if ( saslrc != SASL_OK ) {
+ return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
+ }
+
+ saslrc = sasl_getprop( ctx, SASL_USERNAME, (const void **) &sasl_username );
+ if ( (saslrc == SASL_OK) && sasl_username ) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "SASL identity: %s\n", sasl_username, 0, 0);
+ }
+
+ saslrc = sasl_getprop( ctx, SASL_SSF, (const void **) &ssf );
+ if( saslrc == SASL_OK ) {
+ if( ssf && *ssf ) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "SASL install encryption, for SSF: %lu\n",
+ (unsigned long) *ssf, 0, 0 );
+ nsldapi_sasl_install( ld, NULL );
+ }
+ }
+
+ return( rc );
+}
+#endif /* LDAP_SASLIO_HOOKS */
+
+
+/*
+ * ldap_sasl_bind - authenticate to the ldap server. The dn, mechanism,
+ * and credentials of the entry to which to bind are supplied. An LDAP
+ * error code is returned and if LDAP_SUCCESS is returned *msgidp is set
+ * to the id of the request initiated.
+ *
+ * Example:
+ * struct berval creds;
+ * LDAPControl **ctrls;
+ * int err, msgid;
+ * ... fill in creds with credentials ...
+ * ... fill in ctrls with server controls ...
+ * err = ldap_sasl_bind( ld, "cn=manager, o=university of michigan, c=us",
+ * "mechanismname", &creds, ctrls, NULL, &msgid );
+ */
+int
+LDAP_CALL
+ldap_sasl_bind(
+ LDAP *ld,
+ const char *dn,
+ const char *mechanism,
+ const struct berval *cred,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ int *msgidp
+)
+{
+ BerElement *ber;
+ int rc, simple, msgid, ldapversion;
+
+ /*
+ * The ldapv3 bind request looks like this:
+ * BindRequest ::= SEQUENCE {
+ * version INTEGER,
+ * name DistinguishedName, -- who
+ * authentication CHOICE {
+ * simple [0] OCTET STRING, -- passwd
+ * sasl [3] SaslCredentials -- v3 only
+ * }
+ * }
+ * SaslCredentials ::= SEQUENCE {
+ * mechanism LDAPString,
+ * credentials OCTET STRING
+ * }
+ * all wrapped up in an LDAPMessage sequence.
+ */
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldap_sasl_bind\n", 0, 0, 0 );
+
+ if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
+ return( LDAP_PARAM_ERROR );
+ }
+
+ if ( msgidp == NULL ) {
+ LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
+ return( LDAP_PARAM_ERROR );
+ }
+
+ if ( ( ld->ld_options & LDAP_BITOPT_RECONNECT ) != 0 ) {
+ nsldapi_handle_reconnect( ld );
+ }
+
+ simple = ( mechanism == LDAP_SASL_SIMPLE );
+ ldapversion = NSLDAPI_LDAP_VERSION( ld );
+
+ /* only ldapv3 or higher can do sasl binds */
+ if ( !simple && ldapversion < LDAP_VERSION3 ) {
+ LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
+ return( LDAP_NOT_SUPPORTED );
+ }
+
+ LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK );
+ msgid = ++ld->ld_msgid;
+ LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK );
+
+ if ( dn == NULL )
+ dn = "";
+
+ if ( ld->ld_cache_on && ld->ld_cache_bind != NULL ) {
+ LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK );
+ if ( (rc = (ld->ld_cache_bind)( ld, msgid, LDAP_REQ_BIND, dn,
+ cred, LDAP_AUTH_SASL )) != 0 ) {
+ *msgidp = rc;
+ LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
+ return( LDAP_SUCCESS );
+ }
+ LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
+ }
+
+ /* create a message to send */
+ if (( rc = nsldapi_alloc_ber_with_options( ld, &ber ))
+ != LDAP_SUCCESS ) {
+ return( rc );
+ }
+
+ /* fill it in */
+ if ( simple ) { /* simple bind; works in LDAPv2 or v3 */
+ struct berval tmpcred;
+
+ if ( cred == NULL ) {
+ tmpcred.bv_val = "";
+ tmpcred.bv_len = 0;
+ cred = &tmpcred;
+ }
+ rc = ber_printf( ber, "{it{isto}", msgid, LDAP_REQ_BIND,
+ ldapversion, dn, LDAP_AUTH_SIMPLE, cred->bv_val,
+ cred->bv_len );
+
+ } else { /* SASL bind; requires LDAPv3 or better */
+ if ( cred == NULL || cred->bv_val == NULL || cred->bv_len == 0) {
+ rc = ber_printf( ber, "{it{ist{s}}", msgid,
+ LDAP_REQ_BIND, ldapversion, dn, LDAP_AUTH_SASL,
+ mechanism );
+ } else {
+ rc = ber_printf( ber, "{it{ist{so}}", msgid,
+ LDAP_REQ_BIND, ldapversion, dn, LDAP_AUTH_SASL,
+ mechanism, cred->bv_val,
+ cred->bv_len );
+ }
+ }
+
+ if ( rc == -1 ) {
+ LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL );
+ ber_free( ber, 1 );
+ return( LDAP_ENCODING_ERROR );
+ }
+
+ if ( (rc = nsldapi_put_controls( ld, serverctrls, 1, ber ))
+ != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( rc );
+ }
+
+ /* send the message */
+ rc = nsldapi_send_initial_request( ld, msgid, LDAP_REQ_BIND,
+ (char *)dn, ber );
+ *msgidp = rc;
+ return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS );
+}
+
+/*
+ * ldap_sasl_bind_s - bind to the ldap server using sasl authentication
+ * The dn, mechanism, and credentials of the entry to which to bind are
+ * supplied. LDAP_SUCCESS is returned upon success, the ldap error code
+ * otherwise.
+ *
+ * Example:
+ * struct berval creds;
+ * ... fill in creds with credentials ...
+ * ldap_sasl_bind_s( ld, "cn=manager, o=university of michigan, c=us",
+ * "mechanismname", &creds )
+ */
+int
+LDAP_CALL
+ldap_sasl_bind_s(
+ LDAP *ld,
+ const char *dn,
+ const char *mechanism,
+ const struct berval *cred,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ struct berval **servercredp
+)
+{
+ return ( nsldapi_sasl_bind_s( ld, dn, mechanism, cred,
+ serverctrls, clientctrls, servercredp, NULL ) );
+}
+
+#ifdef LDAP_SASLIO_HOOKS
+/*
+ * SASL Authentication Interface: ldap_sasl_interactive_bind_s
+ *
+ * This routine takes a DN, SASL mech list, and a SASL callback
+ * and performs the necessary sequencing to complete a SASL bind
+ * to the LDAP connection ld. The user provided callback can
+ * use an optionally provided set of default values to complete
+ * any necessary interactions.
+ *
+ * Currently imposes the following restrictions:
+ * A mech list must be provided
+ * LDAP_SASL_INTERACTIVE mode requires a callback
+ */
+int
+LDAP_CALL
+ldap_sasl_interactive_bind_s( LDAP *ld, const char *dn,
+ const char *saslMechanism,
+ LDAPControl **sctrl, LDAPControl **cctrl, unsigned flags,
+ LDAP_SASL_INTERACT_PROC *callback, void *defaults )
+{
+ return ldap_sasl_interactive_bind_ext_s( ld, dn,
+ saslMechanism, sctrl, cctrl, flags, callback,
+ defaults, NULL );
+}
+
+/*
+ * ldap_sasl_interactive_bind_ext_s
+ *
+ * This function extends ldap_sasl_interactive_bind_s by allowing
+ * controls received from the server to be returned as rctrl. The
+ * caller must pass in a valid LDAPControl** pointer and free the
+ * array of controls when finished with them e.g.
+ * LDAPControl **retctrls = NULL;
+ * ...
+ * ldap_sasl_interactive_bind_ext_s(ld, ...., &retctrls);
+ * ...
+ * ldap_controls_free(retctrls);
+ * Only the controls from the server during the last bind step are returned -
+ * intermediate controls (if any, usually not) are discarded.
+ */
+int
+LDAP_CALL
+ldap_sasl_interactive_bind_ext_s( LDAP *ld, const char *dn,
+ const char *saslMechanism,
+ LDAPControl **sctrl, LDAPControl **cctrl, unsigned flags,
+ LDAP_SASL_INTERACT_PROC *callback, void *defaults, LDAPControl ***rctrl )
+{
+#ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
+ char *smechs;
+#endif
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldap_sasl_interactive_bind_s\n", 0, 0, 0 );
+
+ if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
+ return( LDAP_PARAM_ERROR );
+ }
+
+ if ((flags == LDAP_SASL_INTERACTIVE) && (callback == NULL)) {
+ return( LDAP_PARAM_ERROR );
+ }
+
+ LDAP_MUTEX_LOCK(ld, LDAP_SASL_LOCK );
+
+ if( saslMechanism == NULL || *saslMechanism == '\0' ) {
+#ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
+ rc = nsldapi_get_sasl_mechs( ld, &smechs );
+ if( rc != LDAP_SUCCESS ) {
+ LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
+ return( rc );
+ }
+ saslMechanism = smechs;
+#else
+ LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
+ return( LDAP_PARAM_ERROR );
+#endif /* LDAP_SASLIO_GET_MECHS_FROM_SERVER */
+ }
+
+ rc = nsldapi_sasl_do_bind( ld, dn, saslMechanism,
+ flags, callback, defaults, sctrl, cctrl, rctrl);
+
+ LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
+ return( rc );
+}
+#else /* LDAP_SASLIO_HOOKS */
+/* stubs for platforms that do not support SASL */
+int
+LDAP_CALL
+ldap_sasl_interactive_bind_s( LDAP *ld, const char *dn,
+ const char *saslMechanism,
+ LDAPControl **sctrl, LDAPControl **cctrl, unsigned flags,
+ LDAP_SASL_INTERACT_PROC *callback, void *defaults )
+{
+ return LDAP_SUCCESS;
+}
+
+int
+LDAP_CALL
+ldap_sasl_interactive_bind_ext_s( LDAP *ld, const char *dn,
+ const char *saslMechanism,
+ LDAPControl **sctrl, LDAPControl **cctrl, unsigned flags,
+ LDAP_SASL_INTERACT_PROC *callback, void *defaults, LDAPControl ***rctrl )
+{
+ return LDAP_SUCCESS;
+}
+#endif /* LDAP_SASLIO_HOOKS */
+
+
+/* returns an LDAP error code that indicates if parse succeeded or not */
+int
+LDAP_CALL
+ldap_parse_sasl_bind_result(
+ LDAP *ld,
+ LDAPMessage *res,
+ struct berval **servercredp,
+ int freeit
+)
+{
+ BerElement ber;
+ int rc, err;
+ ber_int_t along;
+ ber_len_t len;
+ char *m, *e;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n", 0, 0, 0 );
+
+ /*
+ * the ldapv3 SASL bind response looks like this:
+ *
+ * BindResponse ::= [APPLICATION 1] SEQUENCE {
+ * COMPONENTS OF LDAPResult,
+ * serverSaslCreds [7] OCTET STRING OPTIONAL
+ * }
+ *
+ * all wrapped up in an LDAPMessage sequence.
+ */
+
+ if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) ||
+ !NSLDAPI_VALID_LDAPMESSAGE_BINDRESULT_POINTER( res )) {
+ return( LDAP_PARAM_ERROR );
+ }
+
+ /* only ldapv3 or higher can do sasl binds */
+ if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) {
+ LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
+ return( LDAP_NOT_SUPPORTED );
+ }
+
+ if ( servercredp != NULL ) {
+ *servercredp = NULL;
+ }
+
+ ber = *(res->lm_ber); /* struct copy */
+
+ /* skip past message id, matched dn, error message ... */
+ rc = ber_scanf( &ber, "{iaa}", &along, &m, &e );
+
+ if ( rc != LBER_ERROR &&
+ ber_peek_tag( &ber, &len ) == LDAP_TAG_SASL_RES_CREDS ) {
+ rc = ber_get_stringal( &ber, servercredp );
+ }
+
+ if ( freeit ) {
+ ldap_msgfree( res );
+ }
+
+ if ( rc == LBER_ERROR ) {
+ err = LDAP_DECODING_ERROR;
+ } else {
+ err = (int) along;
+ }
+
+ LDAP_SET_LDERRNO( ld, err, m, e );
+ /* this is a little kludge for the 3.0 Barracuda/hammerhead relese */
+ /* the docs state that the return is either LDAP_DECODING_ERROR */
+ /* or LDAP_SUCCESS. Here we match the docs... it's cleaner in 3.1 */
+
+ if ( LDAP_DECODING_ERROR == err ) {
+ return (LDAP_DECODING_ERROR);
+ } else {
+ return( LDAP_SUCCESS );
+ }
+}
+