📄 bind.c
字号:
/* bind.c - ldap backend bind function *//* $OpenLDAP: pkg/ldap/servers/slapd/back-ldap/bind.c,v 1.85.2.33 2007/01/27 23:56:43 ando Exp $ *//* This work is part of OpenLDAP Software <http://www.openldap.org/>. * * Copyright 1999-2007 The OpenLDAP Foundation. * Portions Copyright 2000-2003 Pierangelo Masarati. * Portions Copyright 1999-2003 Howard Chu. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * <http://www.OpenLDAP.org/license.html>. *//* ACKNOWLEDGEMENTS: * This work was initially developed by Howard Chu for inclusion * in OpenLDAP Software and subsequently enhanced by Pierangelo * Masarati. */#include "portable.h"#include <stdio.h>#include <ac/errno.h>#include <ac/socket.h>#include <ac/string.h>#define AVL_INTERNAL#include "slap.h"#include "back-ldap.h"#undef ldap_debug /* silence a warning in ldap-int.h */#include "../../../libraries/libldap/ldap-int.h"#include "lutil_ldap.h"#define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ "2.16.840.1.113730.3.4.12"#if LDAP_BACK_PRINT_CONNTREE > 0static voidldap_back_ravl_print( Avlnode *root, int depth ){ int i; ldapconn_t *lc; if ( root == 0 ) { return; } ldap_back_ravl_print( root->avl_right, depth+1 ); for ( i = 0; i < depth; i++ ) { fprintf( stderr, "-" ); } lc = root->avl_data; fprintf( stderr, "lc=%p local=\"%s\" conn=%p %s refcnt=%d flags=0x%08x\n", (void *)lc, lc->lc_local_ndn.bv_val ? lc->lc_local_ndn.bv_val : "", (void *)lc->lc_conn, avl_bf2str( root->avl_bf ), lc->lc_refcnt, lc->lc_lcflags ); ldap_back_ravl_print( root->avl_left, depth+1 );}static char* priv2str[] = { "privileged", "privileged/TLS", "anonymous", "anonymous/TLS", "bind", "bind/TLS", NULL};voidldap_back_print_conntree( ldapinfo_t *li, char *msg ){ int c; fprintf( stderr, "========> %s\n", msg ); for ( c = LDAP_BACK_PCONN_FIRST; c < LDAP_BACK_PCONN_LAST; c++ ) { int i = 0; ldapconn_t *lc; fprintf( stderr, " %s[%d]\n", priv2str[ c ], li->li_conn_priv[ c ].lic_num ); LDAP_TAILQ_FOREACH( lc, &li->li_conn_priv[ c ].lic_priv, lc_q ) { fprintf( stderr, " [%d] lc=%p local=\"%s\" conn=%p refcnt=%d flags=0x%08x\n", i, (void *)lc, lc->lc_local_ndn.bv_val ? lc->lc_local_ndn.bv_val : "", (void *)lc->lc_conn, lc->lc_refcnt, lc->lc_lcflags ); i++; } } if ( li->li_conninfo.lai_tree == 0 ) { fprintf( stderr, "\t(empty)\n" ); } else { ldap_back_ravl_print( li->li_conninfo.lai_tree, 0 ); } fprintf( stderr, "<======== %s\n", msg );}#endif /* LDAP_BACK_PRINT_CONNTREE */static intldap_back_freeconn( ldapinfo_t *li, ldapconn_t *lc, int dolock );static ldapconn_t *ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok, struct berval *binddn, struct berval *bindcred );static intldap_back_is_proxy_authz( Operation *op, SlapReply *rs, ldap_back_send_t sendok, struct berval *binddn, struct berval *bindcred );static intldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok, struct berval *binddn, struct berval *bindcred );static intldap_back_prepare_conn( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok );static intldap_back_conndnlc_cmp( const void *c1, const void *c2 );ldapconn_t *ldap_back_conn_delete( ldapinfo_t *li, ldapconn_t *lc ){ if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) { if ( LDAP_BACK_CONN_CACHED( lc ) ) { assert( lc->lc_q.tqe_prev != NULL ); assert( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num > 0 ); li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num--; LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q ); LDAP_TAILQ_ENTRY_INIT( lc, lc_q ); LDAP_BACK_CONN_CACHED_CLEAR( lc ); } else { assert( LDAP_BACK_CONN_TAINTED( lc ) ); assert( lc->lc_q.tqe_prev == NULL ); } } else { ldapconn_t *tmplc = NULL; if ( LDAP_BACK_CONN_CACHED( lc ) ) { assert( !LDAP_BACK_CONN_TAINTED( lc ) ); tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, ldap_back_conndnlc_cmp ); assert( tmplc == lc ); LDAP_BACK_CONN_CACHED_CLEAR( lc ); } assert( LDAP_BACK_CONN_TAINTED( lc ) || tmplc == lc ); } return lc;}intldap_back_bind( Operation *op, SlapReply *rs ){ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; ldapconn_t *lc; int rc = 0; ber_int_t msgid; ldap_back_send_t retrying = LDAP_BACK_RETRYING; lc = ldap_back_getconn( op, rs, LDAP_BACK_BIND_SERR, NULL, NULL ); if ( !lc ) { return rs->sr_err; } /* we can do (almost) whatever we want with this conn, * because either it's temporary, or it's marked as binding */ if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) { ch_free( lc->lc_bound_ndn.bv_val ); BER_BVZERO( &lc->lc_bound_ndn ); } if ( !BER_BVISNULL( &lc->lc_cred ) ) { memset( lc->lc_cred.bv_val, 0, lc->lc_cred.bv_len ); ch_free( lc->lc_cred.bv_val ); BER_BVZERO( &lc->lc_cred ); } LDAP_BACK_CONN_ISBOUND_CLEAR( lc );retry:; /* method is always LDAP_AUTH_SIMPLE if we got here */ rs->sr_err = ldap_sasl_bind( lc->lc_ld, op->o_req_dn.bv_val, LDAP_SASL_SIMPLE, &op->orb_cred, op->o_ctrls, NULL, &msgid ); /* FIXME: should we always retry, or only when piping the bind * in the "override" connection pool? */ rc = ldap_back_op_result( lc, op, rs, msgid, li->li_timeout[ SLAP_OP_BIND ], LDAP_BACK_BIND_SERR | retrying ); if ( rc == LDAP_UNAVAILABLE && retrying ) { retrying &= ~LDAP_BACK_RETRYING; if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_BIND_SERR ) ) { goto retry; } } if ( rc == LDAP_SUCCESS ) { /* If defined, proxyAuthz will be used also when * back-ldap is the authorizing backend; for this * purpose, after a successful bind the connection * is left for further binds, and further operations * on this client connection will use a default * connection with identity assertion */ /* NOTE: use with care */ if ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) { ldap_back_release_conn( li, lc ); return( rc ); } /* rebind is now done inside ldap_back_proxy_authz_bind() * in case of success */ LDAP_BACK_CONN_ISBOUND_SET( lc ); ber_dupbv( &lc->lc_bound_ndn, &op->o_req_ndn ); if ( !BER_BVISNULL( &lc->lc_cred ) ) { memset( lc->lc_cred.bv_val, 0, lc->lc_cred.bv_len ); } if ( LDAP_BACK_SAVECRED( li ) ) { ber_bvreplace( &lc->lc_cred, &op->orb_cred ); ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc ); } else { lc->lc_cred.bv_len = 0; } } /* must re-insert if local DN changed as result of bind */ if ( !LDAP_BACK_CONN_ISBOUND( lc ) || ( !dn_match( &op->o_req_ndn, &lc->lc_local_ndn ) && !LDAP_BACK_PCONN_ISPRIV( lc ) ) ) { int lerr = -1; ldapconn_t *tmplc; /* wait for all other ops to release the connection */retry_lock:; ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex ); if ( lc->lc_refcnt > 1 ) { ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex ); ldap_pvt_thread_yield(); goto retry_lock; }#if LDAP_BACK_PRINT_CONNTREE > 0 ldap_back_print_conntree( li, ">>> ldap_back_bind" );#endif /* LDAP_BACK_PRINT_CONNTREE */ assert( lc->lc_refcnt == 1 ); ldap_back_conn_delete( li, lc ); /* delete all cached connections with the current connection */ if ( LDAP_BACK_SINGLECONN( li ) ) { while ( ( tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, ldap_back_conn_cmp ) ) != NULL ) { Debug( LDAP_DEBUG_TRACE, "=>ldap_back_bind: destroying conn %ld (refcnt=%u)\n", LDAP_BACK_PCONN_ID( lc ), lc->lc_refcnt, 0 ); if ( tmplc->lc_refcnt != 0 ) { /* taint it */ LDAP_BACK_CONN_TAINTED_SET( tmplc ); LDAP_BACK_CONN_CACHED_CLEAR( tmplc ); } else { /* * Needs a test because the handler may be corrupted, * and calling ldap_unbind on a corrupted header results * in a segmentation fault */ ldap_back_conn_free( tmplc ); } } } if ( LDAP_BACK_CONN_ISBOUND( lc ) ) { ber_bvreplace( &lc->lc_local_ndn, &op->o_req_ndn ); if ( be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) { LDAP_BACK_PCONN_ROOTDN_SET( lc, op ); } lerr = avl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc, ldap_back_conndn_cmp, ldap_back_conndn_dup ); }#if LDAP_BACK_PRINT_CONNTREE > 0 ldap_back_print_conntree( li, "<<< ldap_back_bind" );#endif /* LDAP_BACK_PRINT_CONNTREE */ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex ); switch ( lerr ) { case 0: LDAP_BACK_CONN_CACHED_SET( lc ); break; case -1: /* duplicate; someone else successfully bound * on the same connection with the same identity; * we can do this because lc_refcnt == 1 */ ldap_back_conn_free( lc ); lc = NULL; } } if ( lc != NULL ) { ldap_back_release_conn( li, lc ); } return( rc );}/* * ldap_back_conndn_cmp * * compares two ldapconn_t based on the value of the conn pointer * and of the local DN; used by avl stuff for insert, lookup * and direct delete */intldap_back_conndn_cmp( const void *c1, const void *c2 ){ const ldapconn_t *lc1 = (const ldapconn_t *)c1; const ldapconn_t *lc2 = (const ldapconn_t *)c2; int rc; /* If local DNs don't match, it is definitely not a match */ /* For shared sessions, conn is NULL. Only explicitly * bound sessions will have non-NULL conn. */ rc = SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn ); if ( rc == 0 ) { rc = ber_bvcmp( &lc1->lc_local_ndn, &lc2->lc_local_ndn ); } return rc;}/* * ldap_back_conndnlc_cmp * * compares two ldapconn_t based on the value of the conn pointer, * the local DN and the lc pointer; used by avl stuff for insert, lookup * and direct delete */static intldap_back_conndnlc_cmp( const void *c1, const void *c2 ){ const ldapconn_t *lc1 = (const ldapconn_t *)c1; const ldapconn_t *lc2 = (const ldapconn_t *)c2; int rc; /* If local DNs don't match, it is definitely not a match */ /* For shared sessions, conn is NULL. Only explicitly * bound sessions will have non-NULL conn. */ rc = SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn ); if ( rc == 0 ) { rc = ber_bvcmp( &lc1->lc_local_ndn, &lc2->lc_local_ndn ); if ( rc == 0 ) { rc = SLAP_PTRCMP( lc1, lc2 ); } } return rc;}/* * ldap_back_conn_cmp * * compares two ldapconn_t based on the value of the conn pointer; * used by avl stuff for delete of all conns with the same connid */intldap_back_conn_cmp( const void *c1, const void *c2 ){ const ldapconn_t *lc1 = (const ldapconn_t *)c1; const ldapconn_t *lc2 = (const ldapconn_t *)c2; /* For shared sessions, conn is NULL. Only explicitly * bound sessions will have non-NULL conn. */ return SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );}/* * ldap_back_conndn_dup * * returns -1 in case a duplicate ldapconn_t has been inserted; * used by avl stuff */intldap_back_conndn_dup( void *c1, void *c2 ){ ldapconn_t *lc1 = (ldapconn_t *)c1; ldapconn_t *lc2 = (ldapconn_t *)c2; /* Cannot have more than one shared session with same DN */ if ( lc1->lc_conn == lc2->lc_conn && dn_match( &lc1->lc_local_ndn, &lc2->lc_local_ndn ) ) { return -1; } return 0;}static intldap_back_freeconn( ldapinfo_t *li, ldapconn_t *lc, int dolock ){ if ( dolock ) { ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex ); }#if LDAP_BACK_PRINT_CONNTREE > 0 ldap_back_print_conntree( li, ">>> ldap_back_freeconn" );#endif /* LDAP_BACK_PRINT_CONNTREE */ (void)ldap_back_conn_delete( li, lc ); if ( lc->lc_refcnt == 0 ) { ldap_back_conn_free( (void *)lc ); }#if LDAP_BACK_PRINT_CONNTREE > 0 ldap_back_print_conntree( li, "<<< ldap_back_freeconn" );#endif /* LDAP_BACK_PRINT_CONNTREE */ if ( dolock ) { ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex ); } return 0;}#ifdef HAVE_TLSstatic intldap_back_start_tls( LDAP *ld, int protocol, int *is_tls, const char *url, unsigned flags, int retries, const char **text ){ int rc = LDAP_SUCCESS; /* start TLS ("tls-[try-]{start,propagate}" statements) */ if ( ( LDAP_BACK_USE_TLS_F( flags ) || ( *is_tls && LDAP_BACK_PROPAGATE_TLS_F( flags ) ) ) && !ldap_is_ldaps_url( url ) ) {#ifdef SLAP_STARTTLS_ASYNCHRONOUS /* * use asynchronous StartTLS * in case, chase referral (not implemented yet) */ int msgid; if ( protocol == 0 ) { ldap_get_option( ld, LDAP_OPT_PROTOCOL_VERSION, (void *)&protocol ); } if ( protocol < LDAP_VERSION3 ) { /* we should rather bail out... */ rc = LDAP_UNWILLING_TO_PERFORM; *text = "invalid protocol version"; } if ( rc == LDAP_SUCCESS ) { rc = ldap_start_tls( ld, NULL, NULL, &msgid ); } if ( rc == LDAP_SUCCESS ) { LDAPMessage *res = NULL; struct timeval tv; LDAP_BACK_TV_SET( &tv );retry:; rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ); if ( rc < 0 ) { rc = LDAP_UNAVAILABLE; } else if ( rc == 0 ) { if ( retries != LDAP_BACK_RETRY_NEVER ) { ldap_pvt_thread_yield(); if ( retries > 0 ) { retries--; } LDAP_BACK_TV_SET( &tv ); goto retry; } rc = LDAP_UNAVAILABLE; } else if ( rc == LDAP_RES_EXTENDED ) { struct berval *data = NULL; rc = ldap_parse_extended_result( ld, res, NULL, &data, 0 ); if ( rc == LDAP_SUCCESS ) { int err; rc = ldap_parse_result( ld, res, &err, NULL, NULL, NULL, NULL, 1 ); if ( rc == LDAP_SUCCESS ) { rc = err; } res = NULL; /* FIXME: in case a referral * is returned, should we try * using it instead of the * configured URI? */ if ( rc == LDAP_SUCCESS ) { rc = ldap_install_tls( ld ); } else if ( rc == LDAP_REFERRAL ) { rc = LDAP_UNWILLING_TO_PERFORM; *text = "unwilling to chase referral returned by Start TLS exop"; } if ( data ) { if ( data->bv_val ) { ber_memfree( data->bv_val ); } ber_memfree( data ); } } } else { rc = LDAP_OTHER; } if ( res != NULL ) { ldap_msgfree( res ); } }#else /* ! SLAP_STARTTLS_ASYNCHRONOUS */ /* * use synchronous StartTLS */ rc = ldap_start_tls_s( ld, NULL, NULL );#endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */ /* if StartTLS is requested, only attempt it if the URL * is not "ldaps://"; this may occur not only in case * of misconfiguration, but also when used in the chain * overlay, where the "uri" can be parsed out of a referral */ switch ( rc ) { case LDAP_SUCCESS: *is_tls = 1; break; case LDAP_SERVER_DOWN: break; default: if ( LDAP_BACK_TLS_CRITICAL_F( flags ) ) { *text = "could not start TLS"; break; } /* in case Start TLS is not critical */ *is_tls = 0; rc = LDAP_SUCCESS; break; } } else { *is_tls = 0; } return rc;}#endif /* HAVE_TLS */static intldap_back_prepare_conn( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok ){ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; int version; LDAP *ld = NULL;#ifdef HAVE_TLS
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -