+/*
+ * Experimental
+ *
+ * Check if the pattern of an ACL, if any, matches the scope
+ * of the backend it is defined within.
+ */
+#define ACL_SCOPE_UNKNOWN (-2)
+#define ACL_SCOPE_ERR (-1)
+#define ACL_SCOPE_OK (0)
+#define ACL_SCOPE_PARTIAL (1)
+#define ACL_SCOPE_WARN (2)
+
+static int
+check_scope( BackendDB *be, AccessControl *a )
+{
+ ber_len_t patlen;
+ struct berval dn;
+
+ dn = be->be_nsuffix[0];
+
+ if ( BER_BVISEMPTY( &dn ) ) {
+ return ACL_SCOPE_OK;
+ }
+
+ if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
+ a->acl_dn_style != ACL_STYLE_REGEX )
+ {
+ slap_style_t style = a->acl_dn_style;
+
+ if ( style == ACL_STYLE_REGEX ) {
+ char dnbuf[SLAP_LDAPDN_MAXLEN + 2];
+ char rebuf[SLAP_LDAPDN_MAXLEN + 1];
+ ber_len_t rebuflen;
+ regex_t re;
+ int rc;
+
+ /* add trailing '$' to database suffix to form
+ * a simple trial regex pattern "<suffix>$" */
+ AC_MEMCPY( dnbuf, be->be_nsuffix[0].bv_val,
+ be->be_nsuffix[0].bv_len );
+ dnbuf[be->be_nsuffix[0].bv_len] = '$';
+ dnbuf[be->be_nsuffix[0].bv_len + 1] = '\0';
+
+ if ( regcomp( &re, dnbuf, REG_EXTENDED|REG_ICASE ) ) {
+ return ACL_SCOPE_WARN;
+ }
+
+ /* remove trailing ')$', if any, from original
+ * regex pattern */
+ rebuflen = a->acl_dn_pat.bv_len;
+ AC_MEMCPY( rebuf, a->acl_dn_pat.bv_val, rebuflen + 1 );
+ if ( rebuf[rebuflen - 1] == '$' ) {
+ rebuf[--rebuflen] = '\0';
+ }
+ while ( rebuflen > be->be_nsuffix[0].bv_len && rebuf[rebuflen - 1] == ')' ) {
+ rebuf[--rebuflen] = '\0';
+ }
+ if ( rebuflen == be->be_nsuffix[0].bv_len ) {
+ rc = ACL_SCOPE_WARN;
+ goto regex_done;
+ }
+
+ /* not a clear indication of scoping error, though */
+ rc = regexec( &re, rebuf, 0, NULL, 0 )
+ ? ACL_SCOPE_WARN : ACL_SCOPE_OK;
+
+regex_done:;
+ regfree( &re );
+ return rc;
+ }
+
+ patlen = a->acl_dn_pat.bv_len;
+ /* If backend suffix is longer than pattern,
+ * it is a potential mismatch (in the sense
+ * that a superior naming context could
+ * match */
+ if ( dn.bv_len > patlen ) {
+ /* base is blatantly wrong */
+ if ( style == ACL_STYLE_BASE ) return ACL_SCOPE_ERR;
+
+ /* a style of one can be wrong if there is
+ * more than one level between the suffix
+ * and the pattern */
+ if ( style == ACL_STYLE_ONE ) {
+ ber_len_t rdnlen = 0;
+ int sep = 0;
+
+ if ( patlen > 0 ) {
+ if ( !DN_SEPARATOR( dn.bv_val[dn.bv_len - patlen - 1] )) {
+ return ACL_SCOPE_ERR;
+ }
+ sep = 1;
+ }
+
+ rdnlen = dn_rdnlen( NULL, &dn );
+ if ( rdnlen != dn.bv_len - patlen - sep )
+ return ACL_SCOPE_ERR;
+ }
+
+ /* if the trailing part doesn't match,
+ * then it's an error */
+ if ( strcmp( a->acl_dn_pat.bv_val,
+ &dn.bv_val[dn.bv_len - patlen] ) != 0 )
+ {
+ return ACL_SCOPE_ERR;
+ }
+
+ return ACL_SCOPE_PARTIAL;
+ }
+
+ switch ( style ) {
+ case ACL_STYLE_BASE:
+ case ACL_STYLE_ONE:
+ case ACL_STYLE_CHILDREN:
+ case ACL_STYLE_SUBTREE:
+ break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+
+ if ( dn.bv_len < patlen &&
+ !DN_SEPARATOR( a->acl_dn_pat.bv_val[patlen - dn.bv_len - 1] ))
+ {
+ return ACL_SCOPE_ERR;
+ }
+
+ if ( strcmp( &a->acl_dn_pat.bv_val[patlen - dn.bv_len], dn.bv_val )
+ != 0 )
+ {
+ return ACL_SCOPE_ERR;
+ }
+
+ return ACL_SCOPE_OK;
+ }
+
+ return ACL_SCOPE_UNKNOWN;
+}
+