From 1bc2e5533809efb9dd9f2a5e2b9bc7ba64aa41d8 Mon Sep 17 00:00:00 2001 From: Quanah Gibson-Mount Date: Sat, 8 Nov 2008 00:33:06 +0000 Subject: [PATCH] ITS#5704 --- CHANGES | 1 + doc/man/man5/slapo-constraint.5 | 37 +++- servers/slapd/overlays/constraint.c | 328 +++++++++++++++++++++++----- 3 files changed, 308 insertions(+), 58 deletions(-) diff --git a/CHANGES b/CHANGES index ec62e06ba6..5e2b7cbd65 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,7 @@ OpenLDAP 2.4.13 Engineering Fixed liblutil hex conversion (ITS#5699) Added slapd support for certificateListExactMatch from RFC4523 (ITS#5700) Fixed slapd-bdb/hdb invalid db crash (ITS#5698) + Added slapo-constraint parameter to allow LDAP URI constraints (ITS#5704) Added slapo-constraint "set" type (ITS#5702) Added slapo-translucent try local bind when remote fails (ITS#5656) diff --git a/doc/man/man5/slapo-constraint.5 b/doc/man/man5/slapo-constraint.5 index 28e49d40bb..e10a39476d 100644 --- a/doc/man/man5/slapo-constraint.5 +++ b/doc/man/man5/slapo-constraint.5 @@ -27,9 +27,9 @@ It should appear after the .B overlay directive. .TP -.B constraint_attribute [,...] -Specifies the constraint which should apply to the attribute named as -the first parameter. +.B constraint_attribute [,...] [ [...]] +Specifies the constraint which should apply to the comma-separated +attribute list named as the first parameter. Two types of constraint are currently supported - .BR regex , .BR size , @@ -60,6 +60,33 @@ type can be used to enforce a limit on an attribute length, and the .B count type limits the number of values of an attribute. +Extra parameters can occur in any order after those described above. +.RS +.TP +.B : restrict= +.RE + +.RS +This extra parameter allows to restrict the application of the corresponding +constraint only to entries that match the +.IR base , +.I scope +and +.I filter +portions of the LDAP URI. +The +.IR base , +if present, must be within the naming context of the database. +The +.I scope +is only used when the +.I base +is present; it defaults to +.BR base . +The other parameters of the URI are not allowed. +.RE + +.LP Any attempt to add or modify an attribute named as part of the constraint overlay specification which does not fit the constraint listed will fail with a @@ -76,6 +103,7 @@ constraint_attribute title uri ldap:///dc=catalog,dc=example,dc=com?title?sub?(objectClass=titleCatalog) constraint_attribute cn,sn,givenName set "(this/givenName + [ ] + this/sn) & this/cn" + restrict="ldap:///ou=People,dc=example,dc=com??sub?(objectClass=person)" .fi .RE @@ -96,7 +124,8 @@ to be constructed by pairing values of the attributes .B sn and .BR givenName , -separated by a space. +separated by a space, but only for entries derived from the objectClass +.BR person . .RE .SH FILES .TP diff --git a/servers/slapd/overlays/constraint.c b/servers/slapd/overlays/constraint.c index 711790080f..bc32080424 100644 --- a/servers/slapd/overlays/constraint.c +++ b/servers/slapd/overlays/constraint.c @@ -56,6 +56,12 @@ typedef struct constraint { struct constraint *ap_next; AttributeDescription **ap; + + LDAPURLDesc *restrict_lud; + struct berval restrict_ndn; + Filter *restrict_filter; + struct berval restrict_val; + regex_t *re; LDAPURLDesc *lud; int set; @@ -74,10 +80,10 @@ enum { static ConfigDriver constraint_cf_gen; static ConfigTable constraintcfg[] = { - { "constraint_attribute", "attribute> (regex|uri) (regex|uri|set|size|count) []", + 4, 0, 0, ARG_MAGIC | CONSTRAINT_ATTRIBUTE, constraint_cf_gen, "( OLcfgOvAt:13.1 NAME 'olcConstraintAttribute' " - "DESC 'regular expression constraint for attribute' " + "DESC 'constraint for list of attributes' " "EQUALITY caseIgnoreMatch " "SYNTAX OMsDirectoryString )", NULL, NULL }, { NULL, NULL, 0, 0, 0, ARG_IGNORED } @@ -94,8 +100,16 @@ static ConfigOCs constraintocs[] = { }; static void -constraint_free( constraint *cp ) +constraint_free( constraint *cp, int freeme ) { + if (cp->restrict_lud) + ldap_free_urldesc(cp->restrict_lud); + if (!BER_BVISNULL(&cp->restrict_ndn)) + ch_free(cp->restrict_ndn.bv_val); + if (cp->restrict_filter != NULL && cp->restrict_filter != slap_filter_objectClass_pres) + filter_free(cp->restrict_filter); + if (!BER_BVISNULL(&cp->restrict_val)) + ch_free(cp->restrict_val.bv_val); if (cp->re) { regfree(cp->re); ch_free(cp->re); @@ -108,7 +122,8 @@ constraint_free( constraint *cp ) ch_free(cp->attrs); if (cp->ap) ch_free(cp->ap); - ch_free(cp); + if (freeme) + ch_free(cp); } static int @@ -118,7 +133,7 @@ constraint_cf_gen( ConfigArgs *c ) constraint *cn = on->on_bi.bi_private, *cp; struct berval bv; int i, rc = 0; - constraint ap = { NULL, NULL, NULL }, *a2 = NULL; + constraint ap = { NULL }; const char *text = NULL; switch ( c->op ) { @@ -143,6 +158,7 @@ constraint_cf_gen( ConfigArgs *c ) tstr = REGEX_STR; } else if (cp->lud) { tstr = URI_STR; + quotes = 1; } else if (cp->set) { tstr = SET_STR; quotes = 1; @@ -155,6 +171,10 @@ constraint_cf_gen( ConfigArgs *c ) bv.bv_len += strlen(tstr); bv.bv_len += cp->val.bv_len + 2*quotes; + if (cp->restrict_lud != NULL) { + bv.bv_len += cp->restrict_val.bv_len + STRLENOF(" restrict=\"\""); + } + s = bv.bv_val = ch_malloc(bv.bv_len + 1); s = lutil_strncopy( s, cp->ap[0]->ad_cname.bv_val, cp->ap[0]->ad_cname.bv_len ); @@ -168,6 +188,11 @@ constraint_cf_gen( ConfigArgs *c ) if ( quotes ) *s++ = '"'; s = lutil_strncopy( s, cp->val.bv_val, cp->val.bv_len ); if ( quotes ) *s++ = '"'; + if (cp->restrict_lud != NULL) { + s = lutil_strcopy( s, " restrict=\"" ); + s = lutil_strncopy( s, cp->restrict_val.bv_val, cp->restrict_val.bv_len ); + *s++ = '"'; + } *s = '\0'; rc = value_add_one( &c->rvalue_vals, &bv ); @@ -191,7 +216,7 @@ constraint_cf_gen( ConfigArgs *c ) /* zap all constraints */ while (cn) { cp = cn->ap_next; - constraint_free( cn ); + constraint_free( cn, 1 ); cn = cp; } @@ -207,7 +232,7 @@ constraint_cf_gen( ConfigArgs *c ) if (cp) { /* zap cp, and join cpp to cp->ap_next */ *cpp = cp->ap_next; - constraint_free( cp ); + constraint_free( cp, 1 ); } on->on_bi.bi_private = cn; } @@ -232,13 +257,10 @@ constraint_cf_gen( ConfigArgs *c ) if ( slap_str2ad( attrs[j], &ap.ap[j], &text ) ) { snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s <%s>: %s\n", c->argv[0], attrs[j], text ); - Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, - "%s: %s\n", c->log, c->cr_msg, 0 ); - ldap_memvfree((void**)attrs); - return( ARG_BAD_CONF ); + rc = ARG_BAD_CONF; + goto done; } } - ldap_memvfree((void**)attrs); if ( strcasecmp( c->argv[2], REGEX_STR ) == 0) { int err; @@ -251,12 +273,11 @@ constraint_cf_gen( ConfigArgs *c ) regerror( err, ap.re, errmsg, sizeof(errmsg) ); ch_free(ap.re); snprintf( c->cr_msg, sizeof( c->cr_msg ), - "%s %s: Illegal regular expression \"%s\": Error %s", - c->argv[0], c->argv[1], c->argv[3], errmsg); - Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, - "%s: %s\n", c->log, c->cr_msg, 0 ); + "%s %s: Illegal regular expression \"%s\": Error %s", + c->argv[0], c->argv[1], c->argv[3], errmsg); ap.re = NULL; - return( ARG_BAD_CONF ); + rc = ARG_BAD_CONF; + goto done; } ber_str2bv( c->argv[3], 0, 1, &ap.val ); } else if ( strcasecmp( c->argv[2], SIZE_STR ) == 0 ) { @@ -277,21 +298,17 @@ constraint_cf_gen( ConfigArgs *c ) snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s %s: Invalid URI \"%s\"", c->argv[0], c->argv[1], c->argv[3]); - Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, - "%s: %s\n", c->log, c->cr_msg, 0 ); - return( ARG_BAD_CONF ); + rc = ARG_BAD_CONF; + goto done; } if (ap.lud->lud_host != NULL) { snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s %s: unsupported hostname in URI \"%s\"", c->argv[0], c->argv[1], c->argv[3]); - Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, - "%s: %s\n", c->log, c->cr_msg, 0 ); - ldap_free_urldesc(ap.lud); - - return( ARG_BAD_CONF ); + rc = ARG_BAD_CONF; + goto done; } for ( i=0; ap.lud->lud_attrs[i]; i++); @@ -304,23 +321,43 @@ constraint_cf_gen( ConfigArgs *c ) ch_free( ap.attrs ); snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s <%s>: %s\n", c->argv[0], ap.lud->lud_attrs[i], text ); - Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, - "%s: %s\n", c->log, c->cr_msg, 0 ); - return( ARG_BAD_CONF ); + rc = ARG_BAD_CONF; + goto done; } } ap.attrs[i] = NULL; } - if (ap.lud->lud_dn == NULL) + if (ap.lud->lud_dn == NULL) { ap.lud->lud_dn = ch_strdup(""); + } else { + struct berval dn, ndn; + + ber_str2bv( ap.lud->lud_dn, 0, 0, &dn ); + if (dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL ) ) { + /* cleanup */ + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "%s %s: URI %s DN normalization failed", + c->argv[0], c->argv[1], c->argv[3] ); + Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, + "%s: %s\n", c->log, c->cr_msg, 0 ); + rc = ARG_BAD_CONF; + goto done; + } + ldap_memfree( ap.lud->lud_dn ); + ap.lud->lud_dn = ndn.bv_val; + } if (ap.lud->lud_filter == NULL) { ap.lud->lud_filter = ch_strdup("objectClass=*"); } else if ( ap.lud->lud_filter[0] == '(' ) { ber_len_t len = strlen( ap.lud->lud_filter ); if ( ap.lud->lud_filter[len - 1] != ')' ) { - return( ARG_BAD_CONF ); + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "%s %s: invalid URI filter: %s", + c->argv[0], c->argv[1], ap.lud->lud_filter ); + rc = ARG_BAD_CONF; + goto done; } AC_MEMCPY( &ap.lud->lud_filter[0], &ap.lud->lud_filter[1], len - 2 ); ap.lud->lud_filter[len - 2] = '\0'; @@ -334,28 +371,148 @@ constraint_cf_gen( ConfigArgs *c ) } else { snprintf( c->cr_msg, sizeof( c->cr_msg ), - "%s %s: Unknown constraint type: %s", - c->argv[0], c->argv[1], c->argv[2] ); - Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, - "%s: %s\n", c->log, c->cr_msg, 0 ); - return ( ARG_BAD_CONF ); + "%s %s: Unknown constraint type: %s", + c->argv[0], c->argv[1], c->argv[2] ); + rc = ARG_BAD_CONF; + goto done; } - a2 = ch_calloc( sizeof(constraint), 1 ); - a2->ap_next = on->on_bi.bi_private; - a2->ap = ap.ap; - a2->re = ap.re; - a2->val = ap.val; - a2->lud = ap.lud; - a2->set = ap.set; - a2->size = ap.size; - a2->count = ap.count; - if ( a2->lud ) { - ber_str2bv(a2->lud->lud_dn, 0, 0, &a2->dn); - ber_str2bv(a2->lud->lud_filter, 0, 0, &a2->filter); + if ( c->argc > 4 ) { + int argidx; + + for ( argidx = 4; argidx < c->argc; argidx++ ) { + if ( strncasecmp( c->argv[argidx], "restrict=", STRLENOF("restrict=") ) == 0 ) { + int err; + char *arg = c->argv[argidx] + STRLENOF("restrict="); + + err = ldap_url_parse(arg, &ap.restrict_lud); + if ( err != LDAP_URL_SUCCESS ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "%s %s: Invalid restrict URI \"%s\"", + c->argv[0], c->argv[1], arg); + rc = ARG_BAD_CONF; + goto done; + } + + if (ap.restrict_lud->lud_host != NULL) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "%s %s: unsupported hostname in restrict URI \"%s\"", + c->argv[0], c->argv[1], arg); + rc = ARG_BAD_CONF; + goto done; + } + + if ( ap.restrict_lud->lud_attrs != NULL ) { + if ( ap.restrict_lud->lud_attrs[0] != '\0' ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "%s %s: attrs not allowed in restrict URI %s\n", + c->argv[0], c->argv[1], arg); + rc = ARG_BAD_CONF; + goto done; + } + ldap_memvfree((void *)ap.restrict_lud->lud_attrs); + ap.restrict_lud->lud_attrs = NULL; + } + + if (ap.restrict_lud->lud_dn != NULL) { + if (ap.restrict_lud->lud_dn[0] == '\0') { + ldap_memfree(ap.restrict_lud->lud_dn); + ap.restrict_lud->lud_dn = NULL; + + } else { + struct berval dn, ndn; + int j; + + ber_str2bv(ap.restrict_lud->lud_dn, 0, 0, &dn); + if (dnNormalize(0, NULL, NULL, &dn, &ndn, NULL)) { + /* cleanup */ + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "%s %s: restrict URI %s DN normalization failed", + c->argv[0], c->argv[1], arg ); + rc = ARG_BAD_CONF; + goto done; + } + + assert(c->be != NULL); + if (c->be->be_nsuffix == NULL) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "%s %s: restrict URI requires suffix", + c->argv[0], c->argv[1] ); + rc = ARG_BAD_CONF; + goto done; + } + + for ( j = 0; !BER_BVISNULL(&c->be->be_nsuffix[j]); j++) { + if (dnIsSuffix(&ndn, &c->be->be_nsuffix[j])) break; + } + + if (BER_BVISNULL(&c->be->be_nsuffix[j])) { + /* error */ + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "%s %s: restrict URI DN %s not within database naming context(s)", + c->argv[0], c->argv[1], dn.bv_val ); + rc = ARG_BAD_CONF; + goto done; + } + + ap.restrict_ndn = ndn; + } + } + + if (ap.restrict_lud->lud_filter != NULL) { + ap.restrict_filter = str2filter(ap.restrict_lud->lud_filter); + if (ap.restrict_filter == NULL) { + /* error */ + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "%s %s: restrict URI filter %s invalid", + c->argv[0], c->argv[1], ap.restrict_lud->lud_filter ); + rc = ARG_BAD_CONF; + goto done; + } + } + + ber_str2bv(c->argv[argidx], 0, 1, &ap.restrict_val); + + } else { + /* cleanup */ + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "%s %s: unrecognized arg #%d (%s)", + c->argv[0], c->argv[1], argidx, c->argv[argidx] ); + rc = ARG_BAD_CONF; + goto done; + } + } } - a2->attrs = ap.attrs; - on->on_bi.bi_private = a2; + +done:; + if ( rc == LDAP_SUCCESS ) { + constraint *a2 = ch_calloc( sizeof(constraint), 1 ); + a2->ap_next = on->on_bi.bi_private; + a2->ap = ap.ap; + a2->re = ap.re; + a2->val = ap.val; + a2->lud = ap.lud; + a2->set = ap.set; + a2->size = ap.size; + a2->count = ap.count; + if ( a2->lud ) { + ber_str2bv(a2->lud->lud_dn, 0, 0, &a2->dn); + ber_str2bv(a2->lud->lud_filter, 0, 0, &a2->filter); + } + a2->attrs = ap.attrs; + a2->restrict_lud = ap.restrict_lud; + a2->restrict_ndn = ap.restrict_ndn; + a2->restrict_filter = ap.restrict_filter; + a2->restrict_val = ap.restrict_val; + on->on_bi.bi_private = a2; + + } else { + Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, + "%s: %s\n", c->log, c->cr_msg, 0 ); + constraint_free( &ap, 0 ); + } + + ldap_memvfree((void**)attrs); } break; default: abort(); @@ -533,6 +690,61 @@ constraint_count_attr(Entry *e, AttributeDescription *ad) return 0; } +static int +constraint_check_restrict( Operation *op, constraint *c, Entry *e ) +{ + assert( c->restrict_lud != NULL ); + + if ( c->restrict_lud->lud_dn != NULL ) { + int diff = e->e_nname.bv_len - c->restrict_ndn.bv_len; + + if ( diff < 0 ) { + return 0; + } + + if ( c->restrict_lud->lud_scope == LDAP_SCOPE_BASE ) { + return bvmatch( &e->e_nname, &c->restrict_ndn ); + } + + if ( !dnIsSuffix( &e->e_nname, &c->restrict_ndn ) ) { + return 0; + } + + if ( c->restrict_lud->lud_scope != LDAP_SCOPE_SUBTREE ) { + struct berval pdn; + + if ( diff == 0 ) { + return 0; + } + + dnParent( &e->e_nname, &pdn ); + + if ( c->restrict_lud->lud_scope == LDAP_SCOPE_ONELEVEL + && pdn.bv_len != c->restrict_ndn.bv_len ) + { + return 0; + } + } + } + + if ( c->restrict_filter != NULL ) { + int rc; + struct berval save_dn = op->o_dn, save_ndn = op->o_ndn; + + op->o_dn = op->o_bd->be_rootdn; + op->o_ndn = op->o_bd->be_rootndn; + rc = test_filter( op, e, c->restrict_filter ); + op->o_dn = save_dn; + op->o_ndn = save_ndn; + + if ( rc != LDAP_COMPARE_TRUE ) { + return 0; + } + } + + return 1; +} + static int constraint_add( Operation *op, SlapReply *rs ) { @@ -563,7 +775,11 @@ constraint_add( Operation *op, SlapReply *rs ) } if (cp->ap[j] == NULL) continue; if ((b = a->a_vals) == NULL) continue; - + + if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, op->ora_e) == 0) { + continue; + } + Debug(LDAP_DEBUG_TRACE, "==> constraint_add, " "a->a_numvals = %d, cp->count = %d\n", @@ -627,7 +843,7 @@ constraint_modify( Operation *op, SlapReply *rs ) /* Do we need to count attributes? */ for(cp = c; cp; cp = cp->ap_next) { - if (cp->count != 0 || cp->set) { + if (cp->count != 0 || cp->set || cp->restrict_lud != 0) { op->o_bd = on->on_info->oi_origdb; rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &target_entry ); op->o_bd = be; @@ -672,7 +888,11 @@ constraint_modify( Operation *op, SlapReply *rs ) } } if (cp->ap[j] == NULL) continue; - + + if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, target_entry) == 0) { + continue; + } + if (cp->count != 0) { int ca; @@ -829,7 +1049,7 @@ constraint_close( for ( ap = on->on_bi.bi_private; ap; ap = a2 ) { a2 = ap->ap_next; - constraint_free( ap ); + constraint_free( ap, 1 ); } return 0; -- 2.39.5