Back to index

openldap  2.4.31
constraint.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* constraint.c - Overlay to constrain attributes to certain values */
00003 /* 
00004  * Copyright 2003-2004 Hewlett-Packard Company
00005  * Copyright 2007 Emmanuel Dreyfus
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 /*
00017  * Authors: Neil Dunbar <neil.dunbar@hp.com>
00018  *                   Emmannuel Dreyfus <manu@netbsd.org>
00019  */
00020 #include "portable.h"
00021 
00022 #ifdef SLAPD_OVER_CONSTRAINT
00023 
00024 #include <stdio.h>
00025 
00026 #include <ac/string.h>
00027 #include <ac/socket.h>
00028 #include <ac/regex.h>
00029 
00030 #include "lutil.h"
00031 #include "slap.h"
00032 #include "config.h"
00033 
00034 /*
00035  * This overlay limits the values which can be placed into an
00036  * attribute, over and above the limits placed by the schema.
00037  *
00038  * It traps only LDAP adds and modify commands (and only seeks to
00039  * control the add and modify value mods of a modify)
00040  */
00041 
00042 #define REGEX_STR "regex"
00043 #define URI_STR "uri"
00044 #define SET_STR "set"
00045 #define SIZE_STR "size"
00046 #define COUNT_STR "count"
00047 
00048 /*
00049  * Linked list of attribute constraints which we should enforce.
00050  * This is probably a sub optimal structure - some form of sorted
00051  * array would be better if the number of attributes contrained is
00052  * likely to be much bigger than 4 or 5. We stick with a list for
00053  * the moment.
00054  */
00055 
00056 typedef struct constraint {
00057        struct constraint *ap_next;
00058        AttributeDescription **ap;
00059 
00060        LDAPURLDesc *restrict_lud;
00061        struct berval restrict_ndn;
00062        Filter *restrict_filter;
00063        struct berval restrict_val;
00064 
00065        regex_t *re;
00066        LDAPURLDesc *lud;
00067        int set;
00068        size_t size;
00069        size_t count;
00070        AttributeDescription **attrs;
00071        struct berval val; /* constraint value */
00072        struct berval dn;
00073        struct berval filter;
00074 } constraint;
00075 
00076 enum {
00077        CONSTRAINT_ATTRIBUTE = 1
00078 };
00079 
00080 static ConfigDriver constraint_cf_gen;
00081 
00082 static ConfigTable constraintcfg[] = {
00083        { "constraint_attribute", "attribute[list]> (regex|uri|set|size|count) <value> [<restrict URI>]",
00084          4, 0, 0, ARG_MAGIC | CONSTRAINT_ATTRIBUTE, constraint_cf_gen,
00085          "( OLcfgOvAt:13.1 NAME 'olcConstraintAttribute' "
00086          "DESC 'constraint for list of attributes' "
00087          "EQUALITY caseIgnoreMatch "
00088          "SYNTAX OMsDirectoryString )", NULL, NULL },
00089        { NULL, NULL, 0, 0, 0, ARG_IGNORED }
00090 };
00091 
00092 static ConfigOCs constraintocs[] = {
00093        { "( OLcfgOvOc:13.1 "
00094          "NAME 'olcConstraintConfig' "
00095          "DESC 'Constraint overlay configuration' "
00096          "SUP olcOverlayConfig "
00097          "MAY ( olcConstraintAttribute ) )",
00098          Cft_Overlay, constraintcfg },
00099        { NULL, 0, NULL }
00100 };
00101 
00102 static void
00103 constraint_free( constraint *cp, int freeme )
00104 {
00105        if (cp->restrict_lud)
00106               ldap_free_urldesc(cp->restrict_lud);
00107        if (!BER_BVISNULL(&cp->restrict_ndn))
00108               ch_free(cp->restrict_ndn.bv_val);
00109        if (cp->restrict_filter != NULL && cp->restrict_filter != slap_filter_objectClass_pres)
00110               filter_free(cp->restrict_filter);
00111        if (!BER_BVISNULL(&cp->restrict_val))
00112               ch_free(cp->restrict_val.bv_val);
00113        if (cp->re) {
00114               regfree(cp->re);
00115               ch_free(cp->re);
00116        }
00117        if (!BER_BVISNULL(&cp->val))
00118               ch_free(cp->val.bv_val);
00119        if (cp->lud)
00120               ldap_free_urldesc(cp->lud);
00121        if (cp->attrs)
00122               ch_free(cp->attrs);
00123        if (cp->ap)
00124               ch_free(cp->ap);
00125        if (freeme)
00126               ch_free(cp);
00127 }
00128 
00129 static int
00130 constraint_cf_gen( ConfigArgs *c )
00131 {
00132        slap_overinst *on = (slap_overinst *)(c->bi);
00133        constraint *cn = on->on_bi.bi_private, *cp;
00134        struct berval bv;
00135        int i, rc = 0;
00136        constraint ap = { NULL };
00137        const char *text = NULL;
00138        
00139        switch ( c->op ) {
00140        case SLAP_CONFIG_EMIT:
00141               switch (c->type) {
00142               case CONSTRAINT_ATTRIBUTE:
00143                      for (cp=cn; cp; cp=cp->ap_next) {
00144                             char *s;
00145                             char *tstr = NULL;
00146                             int quotes = 0;
00147                             int j;
00148                             size_t val;
00149                             char val_buf[SLAP_TEXT_BUFLEN] = { '\0' };
00150 
00151                             bv.bv_len = STRLENOF("  ");
00152                             for (j = 0; cp->ap[j]; j++) {
00153                                    bv.bv_len += cp->ap[j]->ad_cname.bv_len;
00154                             }
00155 
00156                             /* room for commas */
00157                             bv.bv_len += j - 1;
00158 
00159                             if (cp->re) {
00160                                    tstr = REGEX_STR;
00161                                    quotes = 1;
00162                             } else if (cp->lud) {
00163                                    tstr = URI_STR;
00164                                    quotes = 1;
00165                             } else if (cp->set) {
00166                                    tstr = SET_STR;
00167                                    quotes = 1;
00168                             } else if (cp->size) {
00169                                    tstr = SIZE_STR;
00170                                    val = cp->size;
00171                             } else if (cp->count) {
00172                                    tstr = COUNT_STR;
00173                                    val = cp->count;
00174                             }
00175 
00176                             bv.bv_len += strlen(tstr);
00177                             bv.bv_len += cp->val.bv_len + 2*quotes;
00178 
00179                             if (cp->restrict_lud != NULL) {
00180                                    bv.bv_len += cp->restrict_val.bv_len + STRLENOF(" restrict=\"\"");
00181                             }
00182 
00183                             if (cp->count || cp->size) {
00184                                    int len = snprintf(val_buf, sizeof(val_buf), "%d", val);
00185                                    if (len <= 0) {
00186                                           /* error */
00187                                           return -1;
00188                                    }
00189                                    bv.bv_len += len;
00190                             }
00191 
00192                             s = bv.bv_val = ch_malloc(bv.bv_len + 1);
00193 
00194                             s = lutil_strncopy( s, cp->ap[0]->ad_cname.bv_val, cp->ap[0]->ad_cname.bv_len );
00195                             for (j = 1; cp->ap[j]; j++) {
00196                                    *s++ = ',';
00197                                    s = lutil_strncopy( s, cp->ap[j]->ad_cname.bv_val, cp->ap[j]->ad_cname.bv_len );
00198                             }
00199                             *s++ = ' ';
00200                             s = lutil_strcopy( s, tstr );
00201                             *s++ = ' ';
00202                             if (cp->count || cp->size) {
00203                                    s = lutil_strcopy( s, val_buf );
00204                             } else {
00205                                    if ( quotes ) *s++ = '"';
00206                                    s = lutil_strncopy( s, cp->val.bv_val, cp->val.bv_len );
00207                                    if ( quotes ) *s++ = '"';
00208                             }
00209                             if (cp->restrict_lud != NULL) {
00210                                    s = lutil_strcopy( s, " restrict=\"" );
00211                                    s = lutil_strncopy( s, cp->restrict_val.bv_val, cp->restrict_val.bv_len );
00212                                    *s++ = '"';
00213                             }
00214                             *s = '\0';
00215 
00216                             rc = value_add_one( &c->rvalue_vals, &bv );
00217                             if (rc == LDAP_SUCCESS)
00218                                    rc = value_add_one( &c->rvalue_nvals, &bv );
00219                             ch_free(bv.bv_val);
00220                             if (rc) return rc;
00221                      }
00222                      break;
00223               default:
00224                      abort();
00225                      break;
00226               }
00227               break;
00228        case LDAP_MOD_DELETE:
00229               switch (c->type) {
00230               case CONSTRAINT_ATTRIBUTE:
00231                      if (!cn) break; /* nothing to do */
00232                                    
00233                      if (c->valx < 0) {
00234                             /* zap all constraints */
00235                             while (cn) {
00236                                    cp = cn->ap_next;
00237                                    constraint_free( cn, 1 );
00238                                    cn = cp;
00239                             }
00240                                           
00241                             on->on_bi.bi_private = NULL;
00242                      } else {
00243                             constraint **cpp;
00244                                           
00245                             /* zap constraint numbered 'valx' */
00246                             for(i=0, cp = cn, cpp = &cn;
00247                                    (cp) && (i<c->valx);
00248                                    i++, cpp = &cp->ap_next, cp = *cpp);
00249 
00250                             if (cp) {
00251                                    /* zap cp, and join cpp to cp->ap_next */
00252                                    *cpp = cp->ap_next;
00253                                    constraint_free( cp, 1 );
00254                             }
00255                             on->on_bi.bi_private = cn;
00256                      }
00257                      break;
00258 
00259               default:
00260                      abort();
00261                      break;
00262               }
00263               break;
00264        case SLAP_CONFIG_ADD:
00265        case LDAP_MOD_ADD:
00266               switch (c->type) {
00267               case CONSTRAINT_ATTRIBUTE: {
00268                      int j;
00269                      char **attrs = ldap_str2charray( c->argv[1], "," );
00270 
00271                      for ( j = 0; attrs[j]; j++)
00272                             /* just count */ ;
00273                      ap.ap = ch_calloc( sizeof(AttributeDescription*), j + 1 );
00274                      for ( j = 0; attrs[j]; j++) {
00275                             if ( slap_str2ad( attrs[j], &ap.ap[j], &text ) ) {
00276                                    snprintf( c->cr_msg, sizeof( c->cr_msg ),
00277                                           "%s <%s>: %s\n", c->argv[0], attrs[j], text );
00278                                    rc = ARG_BAD_CONF;
00279                                    goto done;
00280                             }
00281                      }
00282 
00283                      if ( strcasecmp( c->argv[2], REGEX_STR ) == 0) {
00284                             int err;
00285                      
00286                             ap.re = ch_malloc( sizeof(regex_t) );
00287                             if ((err = regcomp( ap.re,
00288                                    c->argv[3], REG_EXTENDED )) != 0) {
00289                                    char errmsg[1024];
00290                                                  
00291                                    regerror( err, ap.re, errmsg, sizeof(errmsg) );
00292                                    ch_free(ap.re);
00293                                    snprintf( c->cr_msg, sizeof( c->cr_msg ),
00294                                           "%s %s: Illegal regular expression \"%s\": Error %s",
00295                                           c->argv[0], c->argv[1], c->argv[3], errmsg);
00296                                    ap.re = NULL;
00297                                    rc = ARG_BAD_CONF;
00298                                    goto done;
00299                             }
00300                             ber_str2bv( c->argv[3], 0, 1, &ap.val );
00301                      } else if ( strcasecmp( c->argv[2], SIZE_STR ) == 0 ) {
00302                             size_t size;
00303 
00304                             if ( ( size = atoi(c->argv[3]) ) != 0 )
00305                                    ap.size = size;      
00306                      } else if ( strcasecmp( c->argv[2], COUNT_STR ) == 0 ) {
00307                             size_t count;
00308 
00309                             if ( ( count = atoi(c->argv[3]) ) != 0 )
00310                                    ap.count = count;    
00311                      } else if ( strcasecmp( c->argv[2], URI_STR ) == 0 ) {
00312                             int err;
00313                      
00314                             err = ldap_url_parse(c->argv[3], &ap.lud);
00315                             if ( err != LDAP_URL_SUCCESS ) {
00316                                    snprintf( c->cr_msg, sizeof( c->cr_msg ),
00317                                           "%s %s: Invalid URI \"%s\"",
00318                                           c->argv[0], c->argv[1], c->argv[3]);
00319                                    rc = ARG_BAD_CONF;
00320                                    goto done;
00321                             }
00322 
00323                             if (ap.lud->lud_host != NULL) {
00324                                    snprintf( c->cr_msg, sizeof( c->cr_msg ),
00325                                           "%s %s: unsupported hostname in URI \"%s\"",
00326                                           c->argv[0], c->argv[1], c->argv[3]);
00327                                    ldap_free_urldesc(ap.lud);
00328                                    rc = ARG_BAD_CONF;
00329                                    goto done;
00330                             }
00331 
00332                             for ( i=0; ap.lud->lud_attrs[i]; i++);
00333                             /* FIXME: This is worthless without at least one attr */
00334                             if ( i ) {
00335                                    ap.attrs = ch_malloc( (i+1)*sizeof(AttributeDescription *));
00336                                    for ( i=0; ap.lud->lud_attrs[i]; i++) {
00337                                           ap.attrs[i] = NULL;
00338                                           if ( slap_str2ad( ap.lud->lud_attrs[i], &ap.attrs[i], &text ) ) {
00339                                                  ch_free( ap.attrs );
00340                                                  snprintf( c->cr_msg, sizeof( c->cr_msg ),
00341                                                         "%s <%s>: %s\n", c->argv[0], ap.lud->lud_attrs[i], text );
00342                                                  rc = ARG_BAD_CONF;
00343                                                  goto done;
00344                                           }
00345                                    }
00346                                    ap.attrs[i] = NULL;
00347                             }
00348 
00349                             if (ap.lud->lud_dn == NULL) {
00350                                    ap.lud->lud_dn = ch_strdup("");
00351                             } else {
00352                                    struct berval dn, ndn;
00353 
00354                                    ber_str2bv( ap.lud->lud_dn, 0, 0, &dn );
00355                                    if (dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL ) ) {
00356                                           /* cleanup */
00357                                           snprintf( c->cr_msg, sizeof( c->cr_msg ),
00358                                                  "%s %s: URI %s DN normalization failed",
00359                                                  c->argv[0], c->argv[1], c->argv[3] );
00360                                           Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
00361                                                     "%s: %s\n", c->log, c->cr_msg, 0 );
00362                                           rc = ARG_BAD_CONF;
00363                                           goto done;
00364                                    }
00365                                    ldap_memfree( ap.lud->lud_dn );
00366                                    ap.lud->lud_dn = ndn.bv_val;
00367                             }
00368 
00369                             if (ap.lud->lud_filter == NULL) {
00370                                    ap.lud->lud_filter = ch_strdup("objectClass=*");
00371                             } else if ( ap.lud->lud_filter[0] == '(' ) {
00372                                    ber_len_t len = strlen( ap.lud->lud_filter );
00373                                    if ( ap.lud->lud_filter[len - 1] != ')' ) {
00374                                           snprintf( c->cr_msg, sizeof( c->cr_msg ),
00375                                                  "%s %s: invalid URI filter: %s",
00376                                                  c->argv[0], c->argv[1], ap.lud->lud_filter );
00377                                           rc = ARG_BAD_CONF;
00378                                           goto done;
00379                                    }
00380                                    AC_MEMCPY( &ap.lud->lud_filter[0], &ap.lud->lud_filter[1], len - 2 );
00381                                    ap.lud->lud_filter[len - 2] = '\0';
00382                             }
00383 
00384                             ber_str2bv( c->argv[3], 0, 1, &ap.val );
00385 
00386                      } else if ( strcasecmp( c->argv[2], SET_STR ) == 0 ) {
00387                             ap.set = 1;
00388                             ber_str2bv( c->argv[3], 0, 1, &ap.val );
00389 
00390                      } else {
00391                             snprintf( c->cr_msg, sizeof( c->cr_msg ),
00392                                    "%s %s: Unknown constraint type: %s",
00393                                    c->argv[0], c->argv[1], c->argv[2] );
00394                             rc = ARG_BAD_CONF;
00395                             goto done;
00396                      }
00397 
00398                      if ( c->argc > 4 ) {
00399                             int argidx;
00400 
00401                             for ( argidx = 4; argidx < c->argc; argidx++ ) {
00402                                    if ( strncasecmp( c->argv[argidx], "restrict=", STRLENOF("restrict=") ) == 0 ) {
00403                                           int err;
00404                                           char *arg = c->argv[argidx] + STRLENOF("restrict=");
00405 
00406                                           err = ldap_url_parse(arg, &ap.restrict_lud);
00407                                           if ( err != LDAP_URL_SUCCESS ) {
00408                                                  snprintf( c->cr_msg, sizeof( c->cr_msg ),
00409                                                         "%s %s: Invalid restrict URI \"%s\"",
00410                                                         c->argv[0], c->argv[1], arg);
00411                                                  rc = ARG_BAD_CONF;
00412                                                  goto done;
00413                                           }
00414 
00415                                           if (ap.restrict_lud->lud_host != NULL) {
00416                                                  snprintf( c->cr_msg, sizeof( c->cr_msg ),
00417                                                         "%s %s: unsupported hostname in restrict URI \"%s\"",
00418                                                         c->argv[0], c->argv[1], arg);
00419                                                  rc = ARG_BAD_CONF;
00420                                                  goto done;
00421                                           }
00422 
00423                                           if ( ap.restrict_lud->lud_attrs != NULL ) {
00424                                                  if ( ap.restrict_lud->lud_attrs[0] != '\0' ) {
00425                                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
00426                                                                "%s %s: attrs not allowed in restrict URI %s\n",
00427                                                                c->argv[0], c->argv[1], arg);
00428                                                         rc = ARG_BAD_CONF;
00429                                                         goto done;
00430                                                  }
00431                                                  ldap_memvfree((void *)ap.restrict_lud->lud_attrs);
00432                                                  ap.restrict_lud->lud_attrs = NULL;
00433                                           }
00434 
00435                                           if (ap.restrict_lud->lud_dn != NULL) {
00436                                                  if (ap.restrict_lud->lud_dn[0] == '\0') {
00437                                                         ldap_memfree(ap.restrict_lud->lud_dn);
00438                                                         ap.restrict_lud->lud_dn = NULL;
00439 
00440                                                  } else {
00441                                                         struct berval dn, ndn;
00442                                                         int j;
00443 
00444                                                         ber_str2bv(ap.restrict_lud->lud_dn, 0, 0, &dn);
00445                                                         if (dnNormalize(0, NULL, NULL, &dn, &ndn, NULL)) {
00446                                                                /* cleanup */
00447                                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
00448                                                                       "%s %s: restrict URI %s DN normalization failed",
00449                                                                       c->argv[0], c->argv[1], arg );
00450                                                                rc = ARG_BAD_CONF;
00451                                                                goto done;
00452                                                         }
00453 
00454                                                         assert(c->be != NULL);
00455                                                         if (c->be->be_nsuffix == NULL) {
00456                                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
00457                                                                       "%s %s: restrict URI requires suffix",
00458                                                                       c->argv[0], c->argv[1] );
00459                                                                rc = ARG_BAD_CONF;
00460                                                                goto done;
00461                                                         }
00462 
00463                                                         for ( j = 0; !BER_BVISNULL(&c->be->be_nsuffix[j]); j++) {
00464                                                                if (dnIsSuffix(&ndn, &c->be->be_nsuffix[j])) break;
00465                                                         }
00466 
00467                                                         if (BER_BVISNULL(&c->be->be_nsuffix[j])) {
00468                                                                /* error */
00469                                                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
00470                                                                       "%s %s: restrict URI DN %s not within database naming context(s)",
00471                                                                       c->argv[0], c->argv[1], dn.bv_val );
00472                                                                rc = ARG_BAD_CONF;
00473                                                                goto done;
00474                                                         }
00475 
00476                                                         ap.restrict_ndn = ndn;
00477                                                  }
00478                                           }
00479 
00480                                           if (ap.restrict_lud->lud_filter != NULL) {
00481                                                  ap.restrict_filter = str2filter(ap.restrict_lud->lud_filter);
00482                                                  if (ap.restrict_filter == NULL) {
00483                                                         /* error */
00484                                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
00485                                                                "%s %s: restrict URI filter %s invalid",
00486                                                                c->argv[0], c->argv[1], ap.restrict_lud->lud_filter );
00487                                                         rc = ARG_BAD_CONF;
00488                                                         goto done;
00489                                                  }
00490                                           }
00491 
00492                                           ber_str2bv(c->argv[argidx] + STRLENOF("restrict="), 0, 1, &ap.restrict_val);
00493 
00494                                    } else {
00495                                           /* cleanup */
00496                                           snprintf( c->cr_msg, sizeof( c->cr_msg ),
00497                                                  "%s %s: unrecognized arg #%d (%s)",
00498                                                  c->argv[0], c->argv[1], argidx, c->argv[argidx] );
00499                                           rc = ARG_BAD_CONF;
00500                                           goto done;
00501                                    }
00502                             }
00503                      }
00504 
00505 done:;
00506                      if ( rc == LDAP_SUCCESS ) {
00507                             constraint *a2 = ch_calloc( sizeof(constraint), 1 );
00508                             a2->ap_next = on->on_bi.bi_private;
00509                             a2->ap = ap.ap;
00510                             a2->re = ap.re;
00511                             a2->val = ap.val;
00512                             a2->lud = ap.lud;
00513                             a2->set = ap.set;
00514                             a2->size = ap.size;
00515                             a2->count = ap.count;
00516                             if ( a2->lud ) {
00517                                    ber_str2bv(a2->lud->lud_dn, 0, 0, &a2->dn);
00518                                    ber_str2bv(a2->lud->lud_filter, 0, 0, &a2->filter);
00519                             }
00520                             a2->attrs = ap.attrs;
00521                             a2->restrict_lud = ap.restrict_lud;
00522                             a2->restrict_ndn = ap.restrict_ndn;
00523                             a2->restrict_filter = ap.restrict_filter;
00524                             a2->restrict_val = ap.restrict_val;
00525                             on->on_bi.bi_private = a2;
00526 
00527                      } else {
00528                             Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
00529                                       "%s: %s\n", c->log, c->cr_msg, 0 );
00530                             constraint_free( &ap, 0 );
00531                      }
00532 
00533                      ldap_memvfree((void**)attrs);
00534                      } break;
00535               default:
00536                      abort();
00537                      break;
00538               }
00539               break;
00540        default:
00541               abort();
00542        }
00543 
00544        return rc;
00545 }
00546 
00547 static int
00548 constraint_uri_cb( Operation *op, SlapReply *rs ) 
00549 {
00550        if(rs->sr_type == REP_SEARCH) {
00551               int *foundp = op->o_callback->sc_private;
00552 
00553               *foundp = 1;
00554 
00555               Debug(LDAP_DEBUG_TRACE, "==> constraint_uri_cb <%s>\n",
00556                      rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
00557        }
00558        return 0;
00559 }
00560 
00561 static int
00562 constraint_violation( constraint *c, struct berval *bv, Operation *op )
00563 {
00564        if ((!c) || (!bv)) return LDAP_SUCCESS;
00565        
00566        if ((c->re) &&
00567               (regexec(c->re, bv->bv_val, 0, NULL, 0) == REG_NOMATCH))
00568               return LDAP_CONSTRAINT_VIOLATION; /* regular expression violation */
00569 
00570        if ((c->size) && (bv->bv_len > c->size))
00571               return LDAP_CONSTRAINT_VIOLATION; /* size violation */
00572 
00573        if (c->lud) {
00574               Operation nop = *op;
00575               slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
00576               slap_callback cb;
00577               int i;
00578               int found = 0;
00579               int rc;
00580               size_t len;
00581               struct berval filterstr;
00582               char *ptr;
00583 
00584               cb.sc_next = NULL;
00585               cb.sc_response = constraint_uri_cb;
00586               cb.sc_cleanup = NULL;
00587               cb.sc_private = &found;
00588 
00589               nop.o_protocol = LDAP_VERSION3;
00590               nop.o_tag = LDAP_REQ_SEARCH;
00591               nop.o_time = slap_get_time();
00592               if (c->lud->lud_dn) {
00593                      struct berval dn;
00594 
00595                      ber_str2bv(c->lud->lud_dn, 0, 0, &dn);
00596                      nop.o_req_dn = dn;
00597                      nop.o_req_ndn = dn;
00598                      nop.o_bd = select_backend(&nop.o_req_ndn, 1 );
00599                      if (!nop.o_bd) {
00600                             return LDAP_NO_SUCH_OBJECT; /* unexpected error */
00601                      }
00602                      if (!nop.o_bd->be_search) {
00603                             return LDAP_OTHER; /* unexpected error */
00604                      }
00605               } else {
00606                      nop.o_req_dn = nop.o_bd->be_nsuffix[0];
00607                      nop.o_req_ndn = nop.o_bd->be_nsuffix[0];
00608                      nop.o_bd = on->on_info->oi_origdb;
00609               }
00610               nop.o_do_not_cache = 1;
00611               nop.o_callback = &cb;
00612 
00613               nop.ors_scope = c->lud->lud_scope;
00614               nop.ors_deref = LDAP_DEREF_NEVER;
00615               nop.ors_slimit = SLAP_NO_LIMIT;
00616               nop.ors_tlimit = SLAP_NO_LIMIT;
00617               nop.ors_limit = NULL;
00618 
00619               nop.ors_attrsonly = 0;
00620               nop.ors_attrs = slap_anlist_no_attrs;
00621 
00622               len = STRLENOF("(&(") + 
00623                        c->filter.bv_len +
00624                        STRLENOF(")(|");
00625 
00626               for (i = 0; c->attrs[i]; i++) {
00627                      len += STRLENOF("(") +
00628                                c->attrs[i]->ad_cname.bv_len +
00629                                STRLENOF("=") + 
00630                                bv->bv_len +
00631                                STRLENOF(")");
00632               }
00633 
00634               len += STRLENOF("))");
00635               filterstr.bv_len = len;
00636               filterstr.bv_val = op->o_tmpalloc(len + 1, op->o_tmpmemctx);
00637 
00638               ptr = filterstr.bv_val +
00639                      snprintf(filterstr.bv_val, len, "(&(%s)(|", c->lud->lud_filter);
00640               for (i = 0; c->attrs[i]; i++) {
00641                      *ptr++ = '(';
00642                      ptr = lutil_strcopy( ptr, c->attrs[i]->ad_cname.bv_val );
00643                      *ptr++ = '=';
00644                      ptr = lutil_strcopy( ptr, bv->bv_val );
00645                      *ptr++ = ')';
00646               }
00647               *ptr++ = ')';
00648               *ptr++ = ')';
00649               *ptr++ = '\0';
00650 
00651               nop.ors_filterstr = filterstr;
00652               nop.ors_filter = str2filter_x(&nop, filterstr.bv_val);
00653               if ( nop.ors_filter == NULL ) {
00654                      Debug( LDAP_DEBUG_ANY,
00655                             "%s constraint_violation uri filter=\"%s\" invalid\n",
00656                             op->o_log_prefix, filterstr.bv_val, 0 );
00657                      rc = LDAP_OTHER;
00658 
00659               } else {
00660                      SlapReply nrs = { REP_RESULT };
00661 
00662                      Debug(LDAP_DEBUG_TRACE, 
00663                             "==> constraint_violation uri filter = %s\n",
00664                             filterstr.bv_val, 0, 0);
00665 
00666                      rc = nop.o_bd->be_search( &nop, &nrs );
00667               
00668                      Debug(LDAP_DEBUG_TRACE, 
00669                             "==> constraint_violation uri rc = %d, found = %d\n",
00670                             rc, found, 0);
00671               }
00672               op->o_tmpfree(filterstr.bv_val, op->o_tmpmemctx);
00673 
00674               if ((rc != LDAP_SUCCESS) && (rc != LDAP_NO_SUCH_OBJECT)) {
00675                      return rc; /* unexpected error */
00676               }
00677 
00678               if (!found)
00679                      return LDAP_CONSTRAINT_VIOLATION; /* constraint violation */
00680        }
00681 
00682        return LDAP_SUCCESS;
00683 }
00684 
00685 static char *
00686 print_message( struct berval *errtext, AttributeDescription *a )
00687 {
00688        char *ret;
00689        int sz;
00690        
00691        sz = errtext->bv_len + sizeof(" on ") + a->ad_cname.bv_len;
00692        ret = ch_malloc(sz);
00693        snprintf( ret, sz, "%s on %s", errtext->bv_val, a->ad_cname.bv_val );
00694        return ret;
00695 }
00696 
00697 static unsigned
00698 constraint_count_attr(Entry *e, AttributeDescription *ad)
00699 {
00700        struct Attribute *a;
00701 
00702        if ((a = attr_find(e->e_attrs, ad)) != NULL)
00703               return a->a_numvals;
00704        return 0;
00705 }
00706 
00707 static int
00708 constraint_check_restrict( Operation *op, constraint *c, Entry *e )
00709 {
00710        assert( c->restrict_lud != NULL );
00711 
00712        if ( c->restrict_lud->lud_dn != NULL ) {
00713               int diff = e->e_nname.bv_len - c->restrict_ndn.bv_len;
00714 
00715               if ( diff < 0 ) {
00716                      return 0;
00717               }
00718 
00719               if ( c->restrict_lud->lud_scope == LDAP_SCOPE_BASE ) {
00720                      return bvmatch( &e->e_nname, &c->restrict_ndn );
00721               }
00722 
00723               if ( !dnIsSuffix( &e->e_nname, &c->restrict_ndn ) ) {
00724                      return 0;
00725               }
00726 
00727               if ( c->restrict_lud->lud_scope != LDAP_SCOPE_SUBTREE ) {
00728                      struct berval pdn;
00729 
00730                      if ( diff == 0 ) {
00731                             return 0;
00732                      }
00733 
00734                      dnParent( &e->e_nname, &pdn );
00735 
00736                      if ( c->restrict_lud->lud_scope == LDAP_SCOPE_ONELEVEL
00737                             && pdn.bv_len != c->restrict_ndn.bv_len )
00738                      {
00739                             return 0;
00740                      }
00741               }
00742        }
00743 
00744        if ( c->restrict_filter != NULL ) {
00745               int rc;
00746               struct berval save_dn = op->o_dn, save_ndn = op->o_ndn;
00747 
00748               op->o_dn = op->o_bd->be_rootdn;
00749               op->o_ndn = op->o_bd->be_rootndn;
00750               rc = test_filter( op, e, c->restrict_filter );
00751               op->o_dn = save_dn;
00752               op->o_ndn = save_ndn;
00753 
00754               if ( rc != LDAP_COMPARE_TRUE ) {
00755                      return 0;
00756               }
00757        }
00758 
00759        return 1;
00760 }
00761 
00762 static int
00763 constraint_add( Operation *op, SlapReply *rs )
00764 {
00765        slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
00766        Attribute *a;
00767        constraint *c = on->on_bi.bi_private, *cp;
00768        BerVarray b = NULL;
00769        int i;
00770        struct berval rsv = BER_BVC("add breaks constraint");
00771        int rc;
00772        char *msg = NULL;
00773 
00774        if (get_relax(op)) {
00775               return SLAP_CB_CONTINUE;
00776        }
00777 
00778        if ((a = op->ora_e->e_attrs) == NULL) {
00779               op->o_bd->bd_info = (BackendInfo *)(on->on_info);
00780               send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
00781                      "constraint_add: no attrs");
00782               return(rs->sr_err);
00783        }
00784 
00785        for(; a; a = a->a_next ) {
00786               /* we don't constrain operational attributes */
00787               if (is_at_operational(a->a_desc->ad_type)) continue;
00788 
00789               for(cp = c; cp; cp = cp->ap_next) {
00790                      int j;
00791                      for (j = 0; cp->ap[j]; j++) {
00792                             if (cp->ap[j] == a->a_desc) break;
00793                      }
00794                      if (cp->ap[j] == NULL) continue;
00795                      if ((b = a->a_vals) == NULL) continue;
00796 
00797                      if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, op->ora_e) == 0) {
00798                             continue;
00799                      }
00800 
00801                      Debug(LDAP_DEBUG_TRACE, 
00802                             "==> constraint_add, "
00803                             "a->a_numvals = %u, cp->count = %lu\n",
00804                             a->a_numvals, (unsigned long) cp->count, 0);
00805 
00806                      if ((cp->count != 0) && (a->a_numvals > cp->count)) {
00807                             rc = LDAP_CONSTRAINT_VIOLATION;
00808                             goto add_violation;
00809                      }
00810 
00811                      for ( i = 0; b[i].bv_val; i++ ) {
00812                             rc = constraint_violation( cp, &b[i], op );
00813                             if ( rc ) {
00814                                    goto add_violation;
00815                             }
00816                      }
00817 
00818                      if (cp->set && acl_match_set(&cp->val, op, op->ora_e, NULL) == 0) {
00819                             rc = LDAP_CONSTRAINT_VIOLATION;
00820                             goto add_violation; /* constraint violation */
00821                      }
00822 
00823               }
00824        }
00825 
00826        /* Default is to just fall through to the normal processing */
00827        return SLAP_CB_CONTINUE;
00828 
00829 add_violation:
00830        op->o_bd->bd_info = (BackendInfo *)(on->on_info);
00831        if (rc == LDAP_CONSTRAINT_VIOLATION ) {
00832               msg = print_message( &rsv, a->a_desc );
00833        }
00834        send_ldap_error(op, rs, rc, msg );
00835        ch_free(msg);
00836        return (rs->sr_err);
00837 }
00838 
00839 
00840 static int
00841 constraint_update( Operation *op, SlapReply *rs )
00842 {
00843        slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
00844        Backend *be = op->o_bd;
00845        constraint *c = on->on_bi.bi_private, *cp;
00846        Entry *target_entry = NULL, *target_entry_copy = NULL;
00847        Modifications *modlist, *m;
00848        BerVarray b = NULL;
00849        int i;
00850        struct berval rsv = BER_BVC("modify breaks constraint");
00851        int rc;
00852        char *msg = NULL;
00853 
00854        if (get_relax(op)) {
00855               return SLAP_CB_CONTINUE;
00856        }
00857 
00858        switch ( op->o_tag ) {
00859        case LDAP_REQ_MODIFY:
00860               modlist = op->orm_modlist;
00861               break;
00862 
00863        case LDAP_REQ_MODRDN:
00864               modlist = op->orr_modlist;
00865               break;
00866 
00867        default:
00868               /* impossible! assert? */
00869               return LDAP_OTHER;
00870        }
00871        
00872        Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "constraint_update()\n", 0,0,0);
00873        if ((m = modlist) == NULL) {
00874               op->o_bd->bd_info = (BackendInfo *)(on->on_info);
00875               send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
00876                                           "constraint_update() got null modlist");
00877               return(rs->sr_err);
00878        }
00879 
00880        /* Do we need to count attributes? */
00881        for(cp = c; cp; cp = cp->ap_next) {
00882               if (cp->count != 0 || cp->set || cp->restrict_lud != 0) {
00883                      op->o_bd = on->on_info->oi_origdb;
00884                      rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &target_entry );
00885                      op->o_bd = be;
00886 
00887                      if (rc != 0 || target_entry == NULL) {
00888                             Debug(LDAP_DEBUG_TRACE, 
00889                                    "==> constraint_update rc = %d DN=\"%s\"%s\n",
00890                                    rc, op->o_req_ndn.bv_val,
00891                                    target_entry ? "" : " not found" );
00892                             if ( rc == 0 ) 
00893                                    rc = LDAP_CONSTRAINT_VIOLATION;
00894                             goto mod_violation;
00895                      }
00896                      break;
00897               }
00898        }
00899 
00900        rc = LDAP_CONSTRAINT_VIOLATION;
00901        for(;m; m = m->sml_next) {
00902               unsigned ce = 0;
00903 
00904               if (is_at_operational( m->sml_desc->ad_type )) continue;
00905 
00906               if ((( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_ADD) &&
00907                      (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_REPLACE) &&
00908                      (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_DELETE))
00909                      continue;
00910               /* we only care about ADD and REPLACE modifications */
00911               /* and DELETE are used to track attribute count */
00912               if ((( b = m->sml_values ) == NULL ) || (b[0].bv_val == NULL))
00913                      continue;
00914 
00915               /* Get this attribute count, if needed */
00916               if (target_entry)
00917                      ce = constraint_count_attr(target_entry, m->sml_desc);
00918 
00919               for(cp = c; cp; cp = cp->ap_next) {
00920                      int j;
00921                      for (j = 0; cp->ap[j]; j++) {
00922                             if (cp->ap[j] == m->sml_desc) {
00923                                    break;
00924                             }
00925                      }
00926                      if (cp->ap[j] == NULL) continue;
00927 
00928                      if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, target_entry) == 0) {
00929                             continue;
00930                      }
00931 
00932                      if (cp->count != 0) {
00933                             unsigned ca;
00934 
00935                             if (m->sml_op == LDAP_MOD_DELETE)
00936                                    ce = 0;
00937 
00938                             for (ca = 0; b[ca].bv_val; ++ca);
00939 
00940                             Debug(LDAP_DEBUG_TRACE, 
00941                                    "==> constraint_update ce = %u, "
00942                                    "ca = %u, cp->count = %lu\n",
00943                                    ce, ca, (unsigned long) cp->count);
00944 
00945                             if (m->sml_op == LDAP_MOD_ADD) {
00946                                    if (ca + ce > cp->count) {
00947                                           rc = LDAP_CONSTRAINT_VIOLATION;
00948                                           goto mod_violation;
00949                                    }
00950                             }
00951                             if (m->sml_op == LDAP_MOD_REPLACE) {
00952                                    if (ca > cp->count) {
00953                                           rc = LDAP_CONSTRAINT_VIOLATION;
00954                                           goto mod_violation;
00955                                    }
00956                                    ce = ca;
00957                             }
00958                      } 
00959 
00960                      /* DELETE are to be ignored beyond this point */
00961                      if (( m->sml_op & LDAP_MOD_OP ) == LDAP_MOD_DELETE)
00962                             continue;
00963 
00964                      for ( i = 0; b[i].bv_val; i++ ) {
00965                             rc = constraint_violation( cp, &b[i], op );
00966                             if ( rc ) {
00967                                    goto mod_violation;
00968                             }
00969                      }
00970 
00971                      if (cp->set && target_entry) {
00972                             if (target_entry_copy == NULL) {
00973                                    Modifications *ml;
00974 
00975                                    target_entry_copy = entry_dup(target_entry);
00976 
00977                                    /* if rename, set the new entry's name
00978                                     * (in normalized form only) */
00979                                    if ( op->o_tag == LDAP_REQ_MODRDN ) {
00980                                           struct berval pdn, ndn = BER_BVNULL;
00981 
00982                                           if ( op->orr_nnewSup ) {
00983                                                  pdn = *op->orr_nnewSup;
00984 
00985                                           } else {
00986                                                  dnParent( &target_entry_copy->e_nname, &pdn );
00987                                           }
00988 
00989                                           build_new_dn( &ndn, &pdn, &op->orr_nnewrdn, NULL ); 
00990 
00991                                           ber_memfree( target_entry_copy->e_nname.bv_val );
00992                                           target_entry_copy->e_nname = ndn;
00993                                           ber_bvreplace( &target_entry_copy->e_name, &ndn );
00994                                    }
00995 
00996                                    /* apply modifications, in an attempt
00997                                     * to estimate what the entry would
00998                                     * look like in case all modifications
00999                                     * pass */
01000                                    for ( ml = modlist; ml; ml = ml->sml_next ) {
01001                                           Modification *mod = &ml->sml_mod;
01002                                           const char *text;
01003                                           char textbuf[SLAP_TEXT_BUFLEN];
01004                                           size_t textlen = sizeof(textbuf);
01005                                           int err;
01006 
01007                                           switch ( mod->sm_op ) {
01008                                           case LDAP_MOD_ADD:
01009                                                  err = modify_add_values( target_entry_copy,
01010                                                         mod, get_permissiveModify(op),
01011                                                         &text, textbuf, textlen );
01012                                                  break;
01013 
01014                                           case LDAP_MOD_DELETE:
01015                                                  err = modify_delete_values( target_entry_copy,
01016                                                         mod, get_permissiveModify(op),
01017                                                         &text, textbuf, textlen );
01018                                                  break;
01019 
01020                                           case LDAP_MOD_REPLACE:
01021                                                  err = modify_replace_values( target_entry_copy,
01022                                                         mod, get_permissiveModify(op),
01023                                                         &text, textbuf, textlen );
01024                                                  break;
01025 
01026                                           case LDAP_MOD_INCREMENT:
01027                                                  err = modify_increment_values( target_entry_copy,
01028                                                         mod, get_permissiveModify(op),
01029                                                         &text, textbuf, textlen );
01030                                                  break;
01031 
01032                                           case SLAP_MOD_SOFTADD:
01033                                                  mod->sm_op = LDAP_MOD_ADD;
01034                                                  err = modify_add_values( target_entry_copy,
01035                                                         mod, get_permissiveModify(op),
01036                                                         &text, textbuf, textlen );
01037                                                  mod->sm_op = SLAP_MOD_SOFTADD;
01038                                                  if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
01039                                                         err = LDAP_SUCCESS;
01040                                                  }
01041                                                  break;
01042 
01043                                           case SLAP_MOD_SOFTDEL:
01044                                                  mod->sm_op = LDAP_MOD_ADD;
01045                                                  err = modify_delete_values( target_entry_copy,
01046                                                         mod, get_permissiveModify(op),
01047                                                         &text, textbuf, textlen );
01048                                                  mod->sm_op = SLAP_MOD_SOFTDEL;
01049                                                  if ( err == LDAP_NO_SUCH_ATTRIBUTE ) {
01050                                                         err = LDAP_SUCCESS;
01051                                                  }
01052                                                  break;
01053 
01054                                           case SLAP_MOD_ADD_IF_NOT_PRESENT:
01055                                                  if ( attr_find( target_entry_copy->e_attrs, mod->sm_desc ) ) {
01056                                                         err = LDAP_SUCCESS;
01057                                                         break;
01058                                                  }
01059                                                  mod->sm_op = LDAP_MOD_ADD;
01060                                                  err = modify_add_values( target_entry_copy,
01061                                                         mod, get_permissiveModify(op),
01062                                                         &text, textbuf, textlen );
01063                                                  mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
01064                                                  break;
01065 
01066                                           default:
01067                                                  err = LDAP_OTHER;
01068                                                  break;
01069                                           }
01070 
01071                                           if ( err != LDAP_SUCCESS ) {
01072                                                  rc = err;
01073                                                  goto mod_violation;
01074                                           }
01075                                    }
01076                             }
01077 
01078                             if ( acl_match_set(&cp->val, op, target_entry_copy, NULL) == 0) {
01079                                    rc = LDAP_CONSTRAINT_VIOLATION;
01080                                    goto mod_violation;
01081                             }
01082                      }
01083               }
01084        }
01085 
01086        if (target_entry) {
01087               op->o_bd = on->on_info->oi_origdb;
01088               be_entry_release_r(op, target_entry);
01089               op->o_bd = be;
01090        }
01091 
01092        if (target_entry_copy) {
01093               entry_free(target_entry_copy);
01094        }
01095 
01096        return SLAP_CB_CONTINUE;
01097 
01098 mod_violation:
01099        /* violation */
01100        if (target_entry) {
01101               op->o_bd = on->on_info->oi_origdb;
01102               be_entry_release_r(op, target_entry);
01103               op->o_bd = be;
01104        }
01105 
01106        if (target_entry_copy) {
01107               entry_free(target_entry_copy);
01108        }
01109 
01110        op->o_bd->bd_info = (BackendInfo *)(on->on_info);
01111        if ( rc == LDAP_CONSTRAINT_VIOLATION ) {
01112               msg = print_message( &rsv, m->sml_desc );
01113        }
01114        send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION, msg );
01115        ch_free(msg);
01116        return (rs->sr_err);
01117 }
01118 
01119 static int
01120 constraint_close(
01121        BackendDB *be,
01122        ConfigReply *cr )
01123 {
01124        slap_overinst *on = (slap_overinst *) be->bd_info;
01125        constraint *ap, *a2;
01126 
01127        for ( ap = on->on_bi.bi_private; ap; ap = a2 ) {
01128               a2 = ap->ap_next;
01129               constraint_free( ap, 1 );
01130        }
01131 
01132        return 0;
01133 }
01134 
01135 static slap_overinst constraint_ovl;
01136 
01137 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC
01138 static
01139 #endif
01140 int
01141 constraint_initialize( void ) {
01142        int rc;
01143 
01144        constraint_ovl.on_bi.bi_type = "constraint";
01145        constraint_ovl.on_bi.bi_db_close = constraint_close;
01146        constraint_ovl.on_bi.bi_op_add = constraint_add;
01147        constraint_ovl.on_bi.bi_op_modify = constraint_update;
01148        constraint_ovl.on_bi.bi_op_modrdn = constraint_update;
01149 
01150        constraint_ovl.on_bi.bi_private = NULL;
01151        
01152        constraint_ovl.on_bi.bi_cf_ocs = constraintocs;
01153        rc = config_register_schema( constraintcfg, constraintocs );
01154        if (rc) return rc;
01155        
01156        return overlay_register( &constraint_ovl );
01157 }
01158 
01159 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC
01160 int init_module(int argc, char *argv[]) {
01161        return constraint_initialize();
01162 }
01163 #endif
01164 
01165 #endif /* defined(SLAPD_OVER_CONSTRAINT) */
01166