openldap/servers/slapd/bind.c
Kurt Zeilenga 97bc107537 Add macros to support testing of error categories to ldap.h
Modify ldap_result to assert returned error is not an one reserved
for API use.
Modify frontend LDAP operation routines to return an error code.
The returned value will be used to determine if an unsolicited notification
should be sent to the client.
Need to review returned error codes.  Namely some LDAP_PROTOCOL_ERROR
will like need to be changed (as they will cause unsolicited notifications).
1999-07-01 21:20:45 +00:00

277 lines
6.2 KiB
C

/* bind.c - decode an ldap bind operation and pass it to a backend db */
/*
* Copyright (c) 1995 Regents of the University of Michigan.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that this notice is preserved and that due credit is given
* to the University of Michigan at Ann Arbor. The name of the University
* may not be used to endorse or promote products derived from this
* software without specific prior written permission. This software
* is provided ``as is'' without express or implied warranty.
*/
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include <ac/socket.h>
#include "slap.h"
char *supportedSASLMechanisms[] = {
"X-CRAM-MD5",
"X-DIGEST-MD5",
NULL
};
int
do_bind(
Connection *conn,
Operation *op
)
{
BerElement *ber = op->o_ber;
ber_int_t version;
ber_tag_t method;
char *mech;
char *cdn, *ndn;
ber_tag_t tag;
int rc;
struct berval cred;
Backend *be;
Debug( LDAP_DEBUG_TRACE, "do_bind\n", 0, 0, 0 );
cdn = NULL;
ndn = NULL;
mech = NULL;
cred.bv_val = NULL;
/*
* Parse the bind request. It looks like this:
*
* BindRequest ::= SEQUENCE {
* version INTEGER, -- version
* name DistinguishedName, -- dn
* authentication CHOICE {
* simple [0] OCTET STRING -- passwd
* krbv42ldap [1] OCTET STRING
* krbv42dsa [1] OCTET STRING
* }
* }
*/
tag = ber_scanf( ber, "{iat" /*}*/, &version, &cdn, &method );
if ( tag == LBER_ERROR ) {
Debug( LDAP_DEBUG_ANY, "bind: ber_scanf failed\n", 0, 0, 0 );
send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL,
"decoding error" );
goto cleanup;
}
if( method != LDAP_AUTH_SASL ) {
tag = ber_scanf( ber, /*{*/ "o}", &cred );
} else {
tag = ber_scanf( ber, "{a" /*}*/, &mech );
if ( tag != LBER_ERROR ) {
ber_len_t len;
tag = ber_peek_tag( ber, &len );
if ( tag == LDAP_TAG_LDAPCRED ) {
tag = ber_scanf( ber, "o", &cred );
}
if ( tag != LBER_ERROR ) {
tag = ber_scanf( ber, /*{{*/ "}}" );
}
}
}
if ( tag == LBER_ERROR ) {
send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL,
"decoding error" );
goto cleanup;
}
#ifdef GET_CTRLS
if( (rc = get_ctrls( conn, op, 1 )) != LDAP_SUCCESS ) {
Debug( LDAP_DEBUG_ANY, "do_bind: get_ctrls failed\n", 0, 0, 0 );
goto cleanup;
}
#endif
if( method == LDAP_AUTH_SASL ) {
Debug( LDAP_DEBUG_TRACE, "do_sasl_bind: dn (%s) mech %s\n",
cdn, mech, NULL );
} else {
Debug( LDAP_DEBUG_TRACE, "do_bind: version %d dn (%s) method %d\n",
version, cdn, method );
}
ndn = dn_normalize_case( ch_strdup( cdn ) );
Statslog( LDAP_DEBUG_STATS, "conn=%d op=%d BIND dn=\"%s\" method=%d\n",
conn->c_connid, op->o_opid, ndn, method, 0 );
if ( version < LDAP_VERSION_MIN || version > LDAP_VERSION_MAX ) {
Debug( LDAP_DEBUG_ANY, "unknown version %d\n", version, 0, 0 );
send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL,
"version not supported" );
goto cleanup;
}
if ( method == LDAP_AUTH_SASL ) {
if ( version < LDAP_VERSION3 ) {
Debug( LDAP_DEBUG_ANY, "do_bind: sasl with LDAPv%d\n",
version, 0, 0 );
send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR, NULL,
"sasl bind requires LDAPv3" );
goto cleanup;
}
if( mech == NULL || *mech == '\0' ) {
Debug( LDAP_DEBUG_ANY,
"do_bind: no sasl mechanism provided\n",
version, 0, 0 );
send_ldap_result( conn, op, rc = LDAP_AUTH_METHOD_NOT_SUPPORTED,
NULL, "no sasl mechanism provided" );
goto cleanup;
}
if( !charray_inlist( supportedSASLMechanisms, mech ) ) {
Debug( LDAP_DEBUG_ANY,
"do_bind: sasl mechanism \"%s\" not supported.\n",
mech, 0, 0 );
send_ldap_result( conn, op, rc = LDAP_AUTH_METHOD_NOT_SUPPORTED,
NULL, "sasl mechanism not supported" );
goto cleanup;
}
}
/* accept null binds */
if ( ndn == NULL || *ndn == '\0' ) {
ldap_pvt_thread_mutex_lock( &conn->c_mutex );
conn->c_protocol = version;
if ( conn->c_cdn != NULL ) {
free( conn->c_cdn );
conn->c_cdn = NULL;
}
if ( conn->c_dn != NULL ) {
free( conn->c_dn );
conn->c_dn = NULL;
}
ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
send_ldap_result( conn, op, LDAP_SUCCESS, NULL, NULL );
goto cleanup;
}
/*
* We could be serving multiple database backends. Select the
* appropriate one, or send a referral to our "referral server"
* if we don't hold it.
*/
if ( (be = select_backend( ndn )) == NULL ) {
if ( cred.bv_len == 0 ) {
ldap_pvt_thread_mutex_lock( &conn->c_mutex );
conn->c_protocol = version;
if ( conn->c_cdn != NULL ) {
free( conn->c_cdn );
conn->c_cdn = NULL;
}
if ( conn->c_dn != NULL ) {
free( conn->c_dn );
conn->c_dn = NULL;
}
ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
send_ldap_result( conn, op, LDAP_SUCCESS,
NULL, NULL );
} else if ( default_referral && *default_referral ) {
send_ldap_result( conn, op, rc = LDAP_PARTIAL_RESULTS,
NULL, default_referral );
} else {
send_ldap_result( conn, op, rc = LDAP_INVALID_CREDENTIALS,
NULL, default_referral );
}
goto cleanup;
}
if ( be->be_bind ) {
/* alias suffix */
char *edn;
ndn = suffixAlias( ndn, op, be );
if ( (*be->be_bind)( be, conn, op, ndn, method, mech, &cred, &edn ) == 0 ) {
ldap_pvt_thread_mutex_lock( &conn->c_mutex );
conn->c_protocol = version;
if ( conn->c_cdn != NULL ) {
free( conn->c_cdn );
}
conn->c_cdn = cdn;
cdn = NULL;
if ( conn->c_dn != NULL ) {
free( conn->c_dn );
}
if(edn != NULL) {
conn->c_dn = edn;
} else {
conn->c_dn = ndn;
ndn = NULL;
}
Debug( LDAP_DEBUG_TRACE, "do_bind: bound \"%s\" to \"%s\"\n",
conn->c_cdn, conn->c_dn, method );
ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
/* send this here to avoid a race condition */
send_ldap_result( conn, op, LDAP_SUCCESS, NULL, NULL );
} else if (edn != NULL) {
free( edn );
}
} else {
send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM, NULL,
"Function not implemented" );
}
cleanup:
if( cdn != NULL ) {
free( cdn );
}
if( ndn != NULL ) {
free( ndn );
}
if ( mech != NULL ) {
free( mech );
}
if ( cred.bv_val != NULL ) {
free( cred.bv_val );
}
return rc;
}