878 lines
28 KiB
C
878 lines
28 KiB
C
/* ***** 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 );
|
|
}
|
|
}
|
|
|