/* * dnParent - dn's parent, in-place * note: the incoming dn is assumed to be normalized/prettyfied, * so that escaped rdn/ava separators are in '\'+hexpair form * * note: "dn" and "pdn" can point to the same berval; * beware that, in this case, the pointer to the original buffer * will get lost. */ void dnParent( struct berval *dn, struct berval *pdn ) { char *p; p = ber_bvchr( dn, ',' ); /* one-level dn */ if ( p == NULL ) { pdn->bv_val = dn->bv_val + dn->bv_len; pdn->bv_len = 0; return; } assert( DN_SEPARATOR( p[ 0 ] ) ); p++; assert( ATTR_LEADCHAR( p[ 0 ] ) ); pdn->bv_len = dn->bv_len - (p - dn->bv_val); pdn->bv_val = p; return; }
/* * dnIsSuffix - tells whether suffix is a suffix of dn. * Both dn and suffix must be normalized. */ int dnIsSuffix( const struct berval *dn, const struct berval *suffix ) { int d; assert( dn != NULL ); assert( suffix != NULL ); d = dn->bv_len - suffix->bv_len; /* empty suffix matches any dn */ if ( suffix->bv_len == 0 ) { return 1; } /* suffix longer than dn */ if ( d < 0 ) { return 0; } /* no rdn separator or escaped rdn separator */ if ( d > 1 && !DN_SEPARATOR( dn->bv_val[ d - 1 ] ) ) { return 0; } /* no possible match or malformed dn */ if ( d == 1 ) { return 0; } /* compare */ return( strncmp( dn->bv_val + d, suffix->bv_val, suffix->bv_len ) == 0 ); }
int dnIsOneLevelRDN( struct berval *rdn ) { ber_len_t len = rdn->bv_len; for ( ; len--; ) { if ( DN_SEPARATOR( rdn->bv_val[ len ] ) ) { return 0; } } return 1; }
int bdb_dn2id_matched( BackendDB *be, DB_TXN *txn, struct berval *in, ID *id, ID *id2, int flags ) { struct bdb_info *bdb = (struct bdb_info *) be->be_private; struct berval rdn; char *p1, *p2; idNode *n, *p; int rc = 0; if (!bdb->bi_troot) return DB_NOTFOUND; p = bdb->bi_troot; if (be_issuffix(be, in)) { *id = p->i_id; return 0; } p1 = in->bv_val + in->bv_len - be->be_nsuffix[0].bv_len - 1; n = p; ldap_pvt_thread_rdwr_rlock(&bdb->bi_tree_rdwr); for (;;) { for (p2 = p1-1; (p2 >= in->bv_val) && !DN_SEPARATOR(*p2); p2--); rdn.bv_val = p2+1; rdn.bv_len = p1-rdn.bv_val; p1 = p2; ldap_pvt_thread_rdwr_rlock(&p->i_kids_rdwr); n = bdb_find_rdn_node(&rdn, p->i_kids); ldap_pvt_thread_rdwr_runlock(&p->i_kids_rdwr); if (!n || p2 < in->bv_val) break; p = n; } ldap_pvt_thread_rdwr_runlock(&bdb->bi_tree_rdwr); if (n) { *id = n->i_id; } else if (id2) { *id2 = p->i_id; } else { rc = DB_NOTFOUND; } return rc; }
/* * dnRdn - dn's rdn, in-place * note: the incoming dn is assumed to be normalized/prettyfied, * so that escaped rdn/ava separators are in '\'+hexpair form */ void dnRdn( struct berval *dn, struct berval *rdn ) { char *p; *rdn = *dn; p = ber_bvchr( dn, ',' ); /* one-level dn */ if ( p == NULL ) { return; } assert( DN_SEPARATOR( p[ 0 ] ) ); assert( ATTR_LEADCHAR( p[ 1 ] ) ); rdn->bv_len = p - dn->bv_val; return; }
/* * ldap_back_dn_massage * * Aliases the suffix; based on suffix_alias (servers/slapd/suffixalias.c). */ int ldap_back_dn_massage( dncookie *dc, struct berval *odn, struct berval *res ) { int i, src, dst; struct berval pretty = {0,NULL}, *dn = odn; assert( res != NULL ); if ( dn == NULL ) { res->bv_val = NULL; res->bv_len = 0; return 0; } if ( dc->target->mt_rwmap.rwm_suffix_massage == NULL ) { *res = *dn; return 0; } if ( dc->tofrom ) { src = 0 + dc->normalized; dst = 2 + dc->normalized; } else { src = 2 + dc->normalized; dst = 0 + dc->normalized; /* DN from remote server may be in arbitrary form. * Pretty it so we can parse reliably. */ dnPretty( NULL, dn, &pretty, NULL ); if (pretty.bv_val) dn = &pretty; } for ( i = 0; dc->target->mt_rwmap.rwm_suffix_massage[i].bv_val != NULL; i += 4 ) { int aliasLength = dc->target->mt_rwmap.rwm_suffix_massage[i+src].bv_len; int diff = dn->bv_len - aliasLength; if ( diff < 0 ) { /* alias is longer than dn */ continue; } else if ( diff > 0 && ( !DN_SEPARATOR(dn->bv_val[diff-1]))) { /* boundary is not at a DN separator */ continue; /* At a DN Separator */ } if ( !strcmp( dc->target->mt_rwmap.rwm_suffix_massage[i+src].bv_val, &dn->bv_val[diff] ) ) { res->bv_len = diff + dc->target->mt_rwmap.rwm_suffix_massage[i+dst].bv_len; res->bv_val = ch_malloc( res->bv_len + 1 ); strncpy( res->bv_val, dn->bv_val, diff ); strcpy( &res->bv_val[diff], dc->target->mt_rwmap.rwm_suffix_massage[i+dst].bv_val ); Debug( LDAP_DEBUG_ARGS, "ldap_back_dn_massage:" " converted \"%s\" to \"%s\"\n", BER_BVISNULL( dn ) ? "" : dn->bv_val, BER_BVISNULL( res ) ? "" : res->bv_val, 0 ); break; } } if (pretty.bv_val) { ch_free(pretty.bv_val); dn = odn; } /* Nothing matched, just return the original DN */ if (res->bv_val == NULL) { *res = *dn; } return 0; }
static int limits_get( Operation *op, struct slap_limits_set **limit ) { static struct berval empty_dn = BER_BVC( "" ); struct slap_limits **lm; struct berval *ndns[2]; assert( op != NULL ); assert( limit != NULL ); ndns[0] = &op->o_ndn; ndns[1] = &op->o_req_ndn; Debug( LDAP_DEBUG_TRACE, "==> limits_get: %s self=\"%s\" this=\"%s\"\n", op->o_log_prefix, BER_BVISNULL( ndns[0] ) ? "[anonymous]" : ndns[0]->bv_val, BER_BVISNULL( ndns[1] ) ? "" : ndns[1]->bv_val ); /* * default values */ *limit = &op->o_bd->be_def_limit; if ( op->o_bd->be_limits == NULL ) { return( 0 ); } for ( lm = op->o_bd->be_limits; lm[0] != NULL; lm++ ) { unsigned style = lm[0]->lm_flags & SLAP_LIMITS_MASK; unsigned type = lm[0]->lm_flags & SLAP_LIMITS_TYPE_MASK; unsigned isthis = type == SLAP_LIMITS_TYPE_THIS; struct berval *ndn = ndns[isthis]; if ( style == SLAP_LIMITS_ANY ) goto found_any; if ( BER_BVISEMPTY( ndn ) ) { if ( style == SLAP_LIMITS_ANONYMOUS ) goto found_nodn; if ( !isthis ) continue; ndn = &empty_dn; } switch ( style ) { case SLAP_LIMITS_EXACT: if ( type == SLAP_LIMITS_TYPE_GROUP ) { int rc = backend_group( op, NULL, &lm[0]->lm_pat, ndn, lm[0]->lm_group_oc, lm[0]->lm_group_ad ); if ( rc == 0 ) { goto found_group; } } else { if ( dn_match( &lm[0]->lm_pat, ndn ) ) { goto found_dn; } } break; case SLAP_LIMITS_ONE: case SLAP_LIMITS_SUBTREE: case SLAP_LIMITS_CHILDREN: { ber_len_t d; /* ndn shorter than lm_pat */ if ( ndn->bv_len < lm[0]->lm_pat.bv_len ) { break; } d = ndn->bv_len - lm[0]->lm_pat.bv_len; if ( d == 0 ) { /* allow exact match for SUBTREE only */ if ( style != SLAP_LIMITS_SUBTREE ) { break; } } else { /* check for unescaped rdn separator */ if ( !DN_SEPARATOR( ndn->bv_val[d - 1] ) ) { break; } } /* check that ndn ends with lm_pat */ if ( strcmp( lm[0]->lm_pat.bv_val, &ndn->bv_val[d] ) != 0 ) { break; } /* in case of ONE, require exactly one rdn below lm_pat */ if ( style == SLAP_LIMITS_ONE ) { if ( dn_rdnlen( NULL, ndn ) != d - 1 ) { break; } } goto found_dn; } case SLAP_LIMITS_REGEX: if ( regexec( &lm[0]->lm_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) { goto found_dn; } break; case SLAP_LIMITS_ANONYMOUS: break; case SLAP_LIMITS_USERS: found_nodn: Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s\n", dn_source[isthis], limits2str( style ), 0 ); found_any: *limit = &lm[0]->lm_limits; return( 0 ); found_dn: Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s dn=\"%s\"\n", dn_source[isthis], limits2str( style ), lm[0]->lm_pat.bv_val ); *limit = &lm[0]->lm_limits; return( 0 ); found_group: Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=GROUP match=EXACT " "dn=\"%s\" oc=\"%s\" ad=\"%s\"\n", lm[0]->lm_pat.bv_val, lm[0]->lm_group_oc->soc_cname.bv_val, lm[0]->lm_group_ad->ad_cname.bv_val ); *limit = &lm[0]->lm_limits; return( 0 ); default: assert( 0 ); /* unreachable */ return( -1 ); } } return( 0 ); }
/* return IDs from root to parent of DN */ int mdb_dn2sups( Operation *op, MDB_txn *txn, struct berval *in, ID *ids ) { struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private; MDB_cursor *cursor; MDB_dbi dbi = mdb->mi_dn2id; MDB_val key, data; int rc = 0, nrlen; diskNode *d; char *ptr; ID pid, nid; struct berval tmp; Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2sups(\"%s\")\n", in->bv_val ); if ( !in->bv_len ) { goto done; } tmp = *in; nrlen = tmp.bv_len - op->o_bd->be_nsuffix[0].bv_len; tmp.bv_val += nrlen; tmp.bv_len = op->o_bd->be_nsuffix[0].bv_len; nid = 0; key.mv_size = sizeof(ID); rc = mdb_cursor_open( txn, dbi, &cursor ); if ( rc ) goto done; for (;;) { key.mv_data = &pid; pid = nid; data.mv_size = sizeof(diskNode) + tmp.bv_len; d = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx ); d->nrdnlen[1] = tmp.bv_len & 0xff; d->nrdnlen[0] = (tmp.bv_len >> 8) | 0x80; ptr = lutil_strncopy( d->nrdn, tmp.bv_val, tmp.bv_len ); *ptr = '\0'; data.mv_data = d; rc = mdb_cursor_get( cursor, &key, &data, MDB_GET_BOTH ); op->o_tmpfree( d, op->o_tmpmemctx ); if ( rc ) { mdb_cursor_close( cursor ); break; } ptr = (char *) data.mv_data + data.mv_size - 2*sizeof(ID); memcpy( &nid, ptr, sizeof(ID)); if ( pid ) mdb_idl_insert( ids, pid ); if ( tmp.bv_val > in->bv_val ) { for (ptr = tmp.bv_val - 2; ptr > in->bv_val && !DN_SEPARATOR(*ptr); ptr--) /* empty */; if ( ptr >= in->bv_val ) { if (DN_SEPARATOR(*ptr)) ptr++; tmp.bv_len = tmp.bv_val - ptr - 1; tmp.bv_val = ptr; } } else { break; } } done: if( rc != 0 ) { Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2sups: get failed: %s (%d)\n", mdb_strerror( rc ), rc ); } return rc; }
/* return last found ID in *id if no match * If mc is provided, it will be left pointing to the RDN's * record under the parent's ID. If nsubs is provided, return * the number of entries in this entry's subtree. */ int mdb_dn2id( Operation *op, MDB_txn *txn, MDB_cursor *mc, struct berval *in, ID *id, ID *nsubs, struct berval *matched, struct berval *nmatched ) { struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private; MDB_cursor *cursor; MDB_dbi dbi = mdb->mi_dn2id; MDB_val key, data; int rc = 0, nrlen; diskNode *d; char *ptr; char dn[SLAP_LDAPDN_MAXLEN]; ID pid, nid; struct berval tmp; Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id(\"%s\")\n", in->bv_val ? in->bv_val : "" ); if ( matched ) { matched->bv_val = dn + sizeof(dn) - 1; matched->bv_len = 0; *matched->bv_val-- = '\0'; } if ( nmatched ) { nmatched->bv_len = 0; nmatched->bv_val = 0; } if ( !in->bv_len ) { *id = 0; nid = 0; goto done; } tmp = *in; if ( op->o_bd->be_nsuffix[0].bv_len ) { nrlen = tmp.bv_len - op->o_bd->be_nsuffix[0].bv_len; tmp.bv_val += nrlen; tmp.bv_len = op->o_bd->be_nsuffix[0].bv_len; } else { for ( ptr = tmp.bv_val + tmp.bv_len - 1; ptr >= tmp.bv_val; ptr-- ) if (DN_SEPARATOR(*ptr)) break; ptr++; tmp.bv_len -= ptr - tmp.bv_val; tmp.bv_val = ptr; } nid = 0; key.mv_size = sizeof(ID); if ( mc ) { cursor = mc; } else { rc = mdb_cursor_open( txn, dbi, &cursor ); if ( rc ) goto done; } for (;;) { key.mv_data = &pid; pid = nid; data.mv_size = sizeof(diskNode) + tmp.bv_len; d = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx ); d->nrdnlen[1] = tmp.bv_len & 0xff; d->nrdnlen[0] = (tmp.bv_len >> 8) | 0x80; ptr = lutil_strncopy( d->nrdn, tmp.bv_val, tmp.bv_len ); *ptr = '\0'; data.mv_data = d; rc = mdb_cursor_get( cursor, &key, &data, MDB_GET_BOTH ); op->o_tmpfree( d, op->o_tmpmemctx ); if ( rc ) break; ptr = (char *) data.mv_data + data.mv_size - 2*sizeof(ID); memcpy( &nid, ptr, sizeof(ID)); /* grab the non-normalized RDN */ if ( matched ) { int rlen; d = data.mv_data; rlen = data.mv_size - sizeof(diskNode) - tmp.bv_len - sizeof(ID); matched->bv_len += rlen; matched->bv_val -= rlen + 1; ptr = lutil_strcopy( matched->bv_val, d->rdn + tmp.bv_len ); if ( pid ) { *ptr = ','; matched->bv_len++; } } if ( nmatched ) { nmatched->bv_val = tmp.bv_val; } if ( tmp.bv_val > in->bv_val ) { for (ptr = tmp.bv_val - 2; ptr > in->bv_val && !DN_SEPARATOR(*ptr); ptr--) /* empty */; if ( ptr >= in->bv_val ) { if (DN_SEPARATOR(*ptr)) ptr++; tmp.bv_len = tmp.bv_val - ptr - 1; tmp.bv_val = ptr; } } else { break; } } *id = nid; /* return subtree count if requested */ if ( !rc && nsubs ) { ptr = (char *)data.mv_data + data.mv_size - sizeof(ID); memcpy( nsubs, ptr, sizeof( ID )); } if ( !mc ) mdb_cursor_close( cursor ); done: if ( matched ) { if ( matched->bv_len ) { ptr = op->o_tmpalloc( matched->bv_len+1, op->o_tmpmemctx ); strcpy( ptr, matched->bv_val ); matched->bv_val = ptr; } else { if ( BER_BVISEMPTY( &op->o_bd->be_nsuffix[0] ) && !nid ) { ber_dupbv( matched, (struct berval *)&slap_empty_bv ); } else { matched->bv_val = NULL; } } } if ( nmatched ) { if ( nmatched->bv_val ) { nmatched->bv_len = in->bv_len - (nmatched->bv_val - in->bv_val); } else { *nmatched = slap_empty_bv; } } if( rc != 0 ) { Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id: get failed: %s (%d)\n", mdb_strerror( rc ), rc ); } else { Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id: got id=0x%lx\n", nid ); } return rc; }
/* Find the EntryInfo for the requested DN. If the DN cannot be found, return * the info for its closest ancestor. *res should be NULL to process a * complete DN starting from the tree root. Otherwise *res must be the * immediate parent of the requested DN, and only the RDN will be searched. * The EntryInfo is locked upon return and must be unlocked by the caller. */ int bdb_cache_find_ndn( Operation *op, DB_TXN *txn, struct berval *ndn, EntryInfo **res ) { struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; EntryInfo ei, *eip, *ei2; int rc = 0; char *ptr; /* this function is always called with normalized DN */ if ( *res ) { /* we're doing a onelevel search for an RDN */ ei.bei_nrdn.bv_val = ndn->bv_val; ei.bei_nrdn.bv_len = dn_rdnlen( op->o_bd, ndn ); eip = *res; } else { /* we're searching a full DN from the root */ ptr = ndn->bv_val + ndn->bv_len - op->o_bd->be_nsuffix[0].bv_len; ei.bei_nrdn.bv_val = ptr; ei.bei_nrdn.bv_len = op->o_bd->be_nsuffix[0].bv_len; /* Skip to next rdn if suffix is empty */ if ( ei.bei_nrdn.bv_len == 0 ) { for (ptr = ei.bei_nrdn.bv_val - 2; ptr > ndn->bv_val && !DN_SEPARATOR(*ptr); ptr--) /* empty */; if ( ptr >= ndn->bv_val ) { if (DN_SEPARATOR(*ptr)) ptr++; ei.bei_nrdn.bv_len = ei.bei_nrdn.bv_val - ptr; ei.bei_nrdn.bv_val = ptr; } } eip = &bdb->bi_cache.c_dntree; } for ( bdb_cache_entryinfo_lock( eip ); eip; ) { eip->bei_state |= CACHE_ENTRY_REFERENCED; ei.bei_parent = eip; ei2 = (EntryInfo *)avl_find( eip->bei_kids, &ei, bdb_rdn_cmp ); if ( !ei2 ) { DBC *cursor; int len = ei.bei_nrdn.bv_len; if ( BER_BVISEMPTY( ndn )) { *res = eip; return LDAP_SUCCESS; } ei.bei_nrdn.bv_len = ndn->bv_len - (ei.bei_nrdn.bv_val - ndn->bv_val); eip->bei_finders++; bdb_cache_entryinfo_unlock( eip ); BDB_LOG_PRINTF( bdb->bi_dbenv, NULL, "slapd Reading %s", ei.bei_nrdn.bv_val ); cursor = NULL; rc = bdb_dn2id( op, &ei.bei_nrdn, &ei, txn, &cursor ); if (rc) { bdb_cache_entryinfo_lock( eip ); eip->bei_finders--; if ( cursor ) cursor->c_close( cursor ); *res = eip; return rc; } BDB_LOG_PRINTF( bdb->bi_dbenv, NULL, "slapd Read got %s(%d)", ei.bei_nrdn.bv_val, ei.bei_id ); /* DN exists but needs to be added to cache */ ei.bei_nrdn.bv_len = len; rc = bdb_entryinfo_add_internal( bdb, &ei, &ei2 ); /* add_internal left eip and c_rwlock locked */ eip->bei_finders--; ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock ); if ( cursor ) cursor->c_close( cursor ); if ( rc ) { *res = eip; return rc; } } bdb_cache_entryinfo_lock( ei2 ); if ( ei2->bei_state & CACHE_ENTRY_DELETED ) { /* In the midst of deleting? Give it a chance to * complete. */ bdb_cache_entryinfo_unlock( ei2 ); bdb_cache_entryinfo_unlock( eip ); ldap_pvt_thread_yield(); bdb_cache_entryinfo_lock( eip ); *res = eip; return DB_NOTFOUND; } bdb_cache_entryinfo_unlock( eip ); eip = ei2; /* Advance to next lower RDN */ for (ptr = ei.bei_nrdn.bv_val - 2; ptr > ndn->bv_val && !DN_SEPARATOR(*ptr); ptr--) /* empty */; if ( ptr >= ndn->bv_val ) { if (DN_SEPARATOR(*ptr)) ptr++; ei.bei_nrdn.bv_len = ei.bei_nrdn.bv_val - ptr - 1; ei.bei_nrdn.bv_val = ptr; } if ( ptr < ndn->bv_val ) { *res = eip; break; } } return rc; }
/* * dnRelativeMatch routine */ int dnRelativeMatch( int *matchp, slap_mask_t flags, Syntax *syntax, MatchingRule *mr, struct berval *value, void *assertedValue ) { int match; struct berval *asserted = (struct berval *) assertedValue; assert( matchp != NULL ); assert( value != NULL ); assert( assertedValue != NULL ); assert( !BER_BVISNULL( value ) ); assert( !BER_BVISNULL( asserted ) ); if( mr == slap_schema.si_mr_dnSubtreeMatch ) { if( asserted->bv_len > value->bv_len ) { match = -1; } else if ( asserted->bv_len == value->bv_len ) { match = memcmp( value->bv_val, asserted->bv_val, value->bv_len ); } else { if( DN_SEPARATOR( value->bv_val[value->bv_len - asserted->bv_len - 1] )) { match = memcmp( &value->bv_val[value->bv_len - asserted->bv_len], asserted->bv_val, asserted->bv_len ); } else { match = 1; } } *matchp = match; return LDAP_SUCCESS; } if( mr == slap_schema.si_mr_dnSuperiorMatch ) { asserted = value; value = (struct berval *) assertedValue; mr = slap_schema.si_mr_dnSubordinateMatch; } if( mr == slap_schema.si_mr_dnSubordinateMatch ) { if( asserted->bv_len >= value->bv_len ) { match = -1; } else { if( DN_SEPARATOR( value->bv_val[value->bv_len - asserted->bv_len - 1] )) { match = memcmp( &value->bv_val[value->bv_len - asserted->bv_len], asserted->bv_val, asserted->bv_len ); } else { match = 1; } } *matchp = match; return LDAP_SUCCESS; } if( mr == slap_schema.si_mr_dnOneLevelMatch ) { if( asserted->bv_len >= value->bv_len ) { match = -1; } else { if( DN_SEPARATOR( value->bv_val[value->bv_len - asserted->bv_len - 1] )) { match = memcmp( &value->bv_val[value->bv_len - asserted->bv_len], asserted->bv_val, asserted->bv_len ); if( !match ) { struct berval rdn; rdn.bv_val = value->bv_val; rdn.bv_len = value->bv_len - asserted->bv_len - 1; match = dnIsOneLevelRDN( &rdn ) ? 0 : 1; } } else { match = 1; } } *matchp = match; return LDAP_SUCCESS; } /* should not be reachable */ assert( 0 ); return LDAP_OTHER; }