📄 chain.c
字号:
/* chain.c - chain LDAP operations *//* $OpenLDAP: pkg/ldap/servers/slapd/back-ldap/chain.c,v 1.12.2.22 2007/02/01 21:20:21 ando Exp $ *//* This work is part of OpenLDAP Software <http://www.openldap.org/>. * * Copyright 2003-2007 The OpenLDAP Foundation. * Portions Copyright 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 the Howard Chu for inclusion * in OpenLDAP Software. * This work was subsequently modified by Pierangelo Masarati. */#include "portable.h"#include <stdio.h>#include <ac/string.h>#include <ac/socket.h>#include "lutil.h"#include "slap.h"#include "back-ldap.h"#include "config.h"#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR#define SLAP_CHAINING_DEFAULT LDAP_CHAINING_PREFERRED#define SLAP_CH_RESOLVE_SHIFT SLAP_CONTROL_SHIFT#define SLAP_CH_RESOLVE_MASK (0x3 << SLAP_CH_RESOLVE_SHIFT)#define SLAP_CH_RESOLVE_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)#define SLAP_CH_RESOLVE_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)#define SLAP_CH_RESOLVE_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)#define SLAP_CH_RESOLVE_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)#define SLAP_CH_RESOLVE_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)#define SLAP_CH_CONTINUATION_SHIFT (SLAP_CH_RESOLVE_SHIFT + 2)#define SLAP_CH_CONTINUATION_MASK (0x3 << SLAP_CH_CONTINUATION_SHIFT)#define SLAP_CH_CONTINUATION_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)#define SLAP_CH_CONTINUATION_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)#define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)#define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)#define SLAP_CH_CONTINUATION_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)#define o_chaining o_ctrlflag[sc_chainingBehavior]#define get_chaining(op) ((op)->o_chaining & SLAP_CONTROL_MASK)#define get_chainingBehavior(op) ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))#define get_resolveBehavior(op) ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)#define get_continuationBehavior(op) ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)static int sc_chainingBehavior;#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */typedef enum { LDAP_CH_NONE = 0, LDAP_CH_RES, LDAP_CH_ERR} ldap_chain_status_t;static BackendInfo *lback;typedef struct ldap_chain_t { /* * A "template" ldapinfo_t gets all common configuration items; * then, for each configured URI, an entry is created in the tree; * all the specific configuration items get in the current URI * structure. * * Then, for each referral, extract the URI and lookup the * related structure. If configured to do so, allow URIs * not found in the structure to create a temporary one * that chains anonymously; maybe it can also be added to * the tree? Should be all configurable. */ /* "common" configuration info (anything occurring before an "uri") */ ldapinfo_t *lc_common_li; /* current configuration info */ ldapinfo_t *lc_cfg_li; /* tree of configured[/generated?] "uri" info */ ldap_avl_info_t lc_lai; /* max depth in nested referrals chaining */ int lc_max_depth; unsigned lc_flags;#define LDAP_CHAIN_F_NONE (0x00U)#define LDAP_CHAIN_F_CHAINING (0x01U)#define LDAP_CHAIN_F_CACHE_URI (0x02U)#define LDAP_CHAIN_F_RETURN_ERR (0x04U)#define LDAP_CHAIN_ISSET(lc, f) ( ( (lc)->lc_flags & (f) ) == (f) )#define LDAP_CHAIN_CHAINING( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING )#define LDAP_CHAIN_CACHE_URI( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI )#define LDAP_CHAIN_RETURN_ERR( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR )#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR LDAPControl lc_chaining_ctrl; char lc_chaining_ctrlflag;#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */} ldap_chain_t;static int ldap_chain_db_init_common( BackendDB *be );static int ldap_chain_db_init_one( BackendDB *be );static int ldap_chain_db_open_one( BackendDB *be );#define ldap_chain_db_close_one(be) (0)#define ldap_chain_db_destroy_one(be) (lback)->bi_db_destroy( (be) )typedef struct ldap_chain_cb_t { ldap_chain_status_t lb_status; ldap_chain_t *lb_lc; BI_op_func *lb_op_f; int lb_depth;} ldap_chain_cb_t;static intldap_chain_op( Operation *op, SlapReply *rs, BI_op_func *op_f, BerVarray ref, int depth );static intldap_chain_search( Operation *op, SlapReply *rs, BerVarray ref, int depth );static slap_overinst ldapchain;#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIORstatic intchaining_control_add( ldap_chain_t *lc, Operation *op, LDAPControl ***oldctrlsp ){ LDAPControl **ctrls = NULL; int c = 0; *oldctrlsp = op->o_ctrls; /* default chaining control not defined */ if ( !LDAP_CHAIN_CHAINING( lc ) ) { return 0; } /* already present */ if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) { return 0; } /* FIXME: check other incompatibilities */ /* add to other controls */ if ( op->o_ctrls ) { for ( c = 0; op->o_ctrls[ c ]; c++ ) /* count them */ ; } ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 ); ctrls[ 0 ] = &lc->lc_chaining_ctrl; if ( op->o_ctrls ) { for ( c = 0; op->o_ctrls[ c ]; c++ ) { ctrls[ c + 1 ] = op->o_ctrls[ c ]; } } ctrls[ c + 1 ] = NULL; op->o_ctrls = ctrls; op->o_chaining = lc->lc_chaining_ctrlflag; return 0;}static intchaining_control_remove( Operation *op, LDAPControl ***oldctrlsp ){ LDAPControl **oldctrls = *oldctrlsp; /* we assume that the first control is the chaining control * added by the chain overlay, so it's the only one we explicitly * free */ if ( op->o_ctrls != oldctrls ) { assert( op->o_ctrls != NULL ); assert( op->o_ctrls[ 0 ] != NULL ); free( op->o_ctrls ); op->o_chaining = 0; op->o_ctrls = oldctrls; } *oldctrlsp = NULL; return 0;}#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */static intldap_chain_uri_cmp( const void *c1, const void *c2 ){ const ldapinfo_t *li1 = (const ldapinfo_t *)c1; const ldapinfo_t *li2 = (const ldapinfo_t *)c2; assert( li1->li_bvuri != NULL ); assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) ); assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) ); assert( li2->li_bvuri != NULL ); assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) ); assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) ); /* If local DNs don't match, it is definitely not a match */ return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );}static intldap_chain_uri_dup( void *c1, void *c2 ){ ldapinfo_t *li1 = (ldapinfo_t *)c1; ldapinfo_t *li2 = (ldapinfo_t *)c2; assert( li1->li_bvuri != NULL ); assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) ); assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) ); assert( li2->li_bvuri != NULL ); assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) ); assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) ); /* Cannot have more than one shared session with same DN */ if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) { return -1; } return 0;}/* * Search specific response that strips entryDN from entries */static intldap_chain_cb_search_response( Operation *op, SlapReply *rs ){ ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private; assert( op->o_tag == LDAP_REQ_SEARCH ); /* if in error, don't proceed any further */ if ( lb->lb_status == LDAP_CH_ERR ) { return 0; } if ( rs->sr_type == REP_SEARCH ) { Attribute **ap = &rs->sr_entry->e_attrs; for ( ; *ap != NULL; ap = &(*ap)->a_next ) { /* will be generated later by frontend * (a cleaner solution would be that * the frontend checks if it already exists */ if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 ) { Attribute *a = *ap; *ap = (*ap)->a_next; attr_free( a ); /* there SHOULD be one only! */ break; } } /* tell the frontend not to add generated * operational attributes */ rs->sr_flags |= REP_NO_OPERATIONALS; return SLAP_CB_CONTINUE; } else if ( rs->sr_type == REP_SEARCHREF ) { /* if we get it here, it means the library was unable * to chase the referral... */ if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) { rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth ); }#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) { switch ( get_continuationBehavior( op ) ) { case SLAP_CH_RESOLVE_CHAINING_REQUIRED: lb->lb_status = LDAP_CH_ERR; return rs->sr_err = LDAP_X_CANNOT_CHAIN; default: break; } }#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ return SLAP_CB_CONTINUE; } else if ( rs->sr_type == REP_RESULT ) { if ( rs->sr_err == LDAP_REFERRAL && lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) { rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth ); } /* back-ldap tried to send result */ lb->lb_status = LDAP_CH_RES; } return 0;}/* * Dummy response that simply traces if back-ldap tried to send * anything to the client */static intldap_chain_cb_response( Operation *op, SlapReply *rs ){ ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private; /* if in error, don't proceed any further */ if ( lb->lb_status == LDAP_CH_ERR ) { return 0; } if ( rs->sr_type == REP_RESULT ) {retry:; switch ( rs->sr_err ) { case LDAP_COMPARE_TRUE: case LDAP_COMPARE_FALSE: if ( op->o_tag != LDAP_REQ_COMPARE ) { return rs->sr_err; } /* fallthru */ case LDAP_SUCCESS: lb->lb_status = LDAP_CH_RES; break; case LDAP_REFERRAL: if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) { rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth ); goto retry; }#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) { switch ( get_continuationBehavior( op ) ) { case SLAP_CH_RESOLVE_CHAINING_REQUIRED: lb->lb_status = LDAP_CH_ERR; return rs->sr_err = LDAP_X_CANNOT_CHAIN; default: break; } }#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ break; default: return rs->sr_err; } } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH ) { /* strip the entryDN attribute, but keep returning results */ (void)ldap_chain_cb_search_response( op, rs ); } return SLAP_CB_CONTINUE;}static intldap_chain_op( Operation *op, SlapReply *rs, BI_op_func *op_f, BerVarray ref, int depth ){ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private; ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; ldapinfo_t li = { 0 }, *lip = NULL; struct berval bvuri[ 2 ] = { { 0 } }; /* NOTE: returned if ref is empty... */ int rc = LDAP_OTHER, first_rc;#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR LDAPControl **ctrls = NULL; (void)chaining_control_add( lc, op, &ctrls );#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ li.li_bvuri = bvuri; first_rc = -1; for ( ; !BER_BVISNULL( ref ); ref++ ) { SlapReply rs2 = { 0 }; LDAPURLDesc *srv = NULL; struct berval save_req_dn = op->o_req_dn, save_req_ndn = op->o_req_ndn, dn, pdn = BER_BVNULL, ndn = BER_BVNULL; int temporary = 0; /* We're setting the URI of the first referral; * what if there are more?Document: RFC 45114.1.10. Referral ... If the client wishes to progress the operation, it MUST follow the referral by contacting one of the supported services. If multiple URIs are present, the client assumes that any supported URI may be used to progress the operation. * so we actually need to follow exactly one, * and we can assume any is fine. */ /* parse reference and use * proto://[host][:port]/ only */ rc = ldap_url_parse_ext( ref->bv_val, &srv ); if ( rc != LDAP_URL_SUCCESS ) { /* try next */ rc = LDAP_OTHER; continue; } /* normalize DN */ ber_str2bv( srv->lud_dn, 0, 0, &dn ); rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx ); if ( rc == LDAP_SUCCESS ) { /* remove DN essentially because later on * ldap_initialize() will parse the URL * as a comma-separated URL list */ srv->lud_dn = ""; srv->lud_scope = LDAP_SCOPE_DEFAULT; li.li_uri = ldap_url_desc2str( srv ); srv->lud_dn = dn.bv_val; } ldap_free_urldesc( srv ); if ( rc != LDAP_SUCCESS ) { /* try next */ rc = LDAP_OTHER; continue; } if ( li.li_uri == NULL ) { /* try next */ rc = LDAP_OTHER; goto further_cleanup; } op->o_req_dn = pdn; op->o_req_ndn = ndn; ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] ); /* Searches for a ldapinfo in the avl tree */ ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, (caddr_t)&li, ldap_chain_uri_cmp ); ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex ); if ( lip != NULL ) { op->o_bd->be_private = (void *)lip; } else { rc = ldap_chain_db_init_one( op->o_bd ); if ( rc != 0 ) { goto cleanup; } lip = (ldapinfo_t *)op->o_bd->be_private; lip->li_uri = li.li_uri; lip->li_bvuri = bvuri; rc = ldap_chain_db_open_one( op->o_bd ); if ( rc != 0 ) { lip->li_uri = NULL; lip->li_bvuri = NULL; (void)ldap_chain_db_destroy_one( op->o_bd ); goto cleanup; } if ( LDAP_CHAIN_CACHE_URI( lc ) ) { ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) ) { /* someone just inserted another; * don't bother, use this and then * just free it */ temporary = 1; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -