]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/constraint.c
ITS#5327
[openldap] / servers / slapd / overlays / constraint.c
1 /* $OpenLDAP$ */
2 /* constraint.c - Overlay to constrain attributes to certain values */
3 /* 
4  * Copyright 2003-2004 Hewlett-Packard Company
5  * Copyright 2007 Emmanuel Dreyfus
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /*
17  * Authors: Neil Dunbar <neil.dunbar@hp.com>
18  *                      Emmannuel Dreyfus <manu@netbsd.org>
19  */
20 #include "portable.h"
21
22 #ifdef SLAPD_OVER_CONSTRAINT
23
24 #include <stdio.h>
25
26 #include <ac/string.h>
27 #include <ac/socket.h>
28 #include <ac/regex.h>
29
30 #include "lutil.h"
31 #include "slap.h"
32 #include "config.h"
33
34 /*
35  * This overlay limits the values which can be placed into an
36  * attribute, over and above the limits placed by the schema.
37  *
38  * It traps only LDAP adds and modify commands (and only seeks to
39  * control the add and modify value mods of a modify)
40  */
41
42 #define REGEX_STR "regex"
43 #define URI_STR "uri"
44
45 /*
46  * Linked list of attribute constraints which we should enforce.
47  * This is probably a sub optimal structure - some form of sorted
48  * array would be better if the number of attributes contrained is
49  * likely to be much bigger than 4 or 5. We stick with a list for
50  * the moment.
51  */
52
53 typedef struct constraint {
54         struct constraint *ap_next;
55         AttributeDescription *ap;
56         regex_t *re;
57         LDAPURLDesc *lud;
58         AttributeDescription **attrs;
59         struct berval val; /* constraint value */
60         struct berval dn;
61         struct berval filter;
62 } constraint;
63
64 enum {
65         CONSTRAINT_ATTRIBUTE = 1
66 };
67
68 static ConfigDriver constraint_cf_gen;
69
70 static ConfigTable constraintcfg[] = {
71         { "constraint_attribute", "attribute> (regex|uri) <value",
72           4, 4, 0, ARG_MAGIC | CONSTRAINT_ATTRIBUTE, constraint_cf_gen,
73           "( OLcfgOvAt:13.1 NAME 'olcConstraintAttribute' "
74           "DESC 'regular expression constraint for attribute' "
75           "EQUALITY caseIgnoreMatch "
76           "SYNTAX OMsDirectoryString )", NULL, NULL },
77         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
78 };
79
80 static ConfigOCs constraintocs[] = {
81         { "( OLcfgOvOc:13.1 "
82           "NAME 'olcConstraintConfig' "
83           "DESC 'Constraint overlay configuration' "
84           "SUP olcOverlayConfig "
85           "MAY ( olcConstraintAttribute ) )",
86           Cft_Overlay, constraintcfg },
87         { NULL, 0, NULL }
88 };
89
90 static void
91 constraint_free( constraint *cp )
92 {
93         if (cp->re) {
94                 regfree(cp->re);
95                 ch_free(cp->re);
96         }
97         if (!BER_BVISNULL(&cp->val))
98                 ch_free(cp->val.bv_val);
99         if (cp->lud)
100                 ldap_free_urldesc(cp->lud);
101         if (cp->attrs)
102                 ch_free(cp->attrs);
103         ch_free(cp);
104 }
105
106 static int
107 constraint_cf_gen( ConfigArgs *c )
108 {
109         slap_overinst *on = (slap_overinst *)(c->bi);
110         constraint *cn = on->on_bi.bi_private, *cp;
111         struct berval bv;
112         int i, rc = 0;
113         constraint ap = { NULL, NULL, NULL      }, *a2 = NULL;
114         const char *text = NULL;
115         
116         switch ( c->op ) {
117         case SLAP_CONFIG_EMIT:
118                 switch (c->type) {
119                 case CONSTRAINT_ATTRIBUTE:
120                         for (cp=cn; cp; cp=cp->ap_next) {
121                                 int len;
122                                 char *s;
123                                 char *tstr = NULL;
124
125                                 len = cp->ap->ad_cname.bv_len + 3;
126                                 if (cp->re) {
127                                         len += STRLENOF(REGEX_STR);
128                                         tstr = REGEX_STR;
129                                 } else if (cp->lud) {
130                                         len += STRLENOF(URI_STR);
131                                         tstr = URI_STR;
132                                 }
133                                 len += cp->val.bv_len;
134
135                                 s = ch_malloc(len);
136
137                                 bv.bv_len = snprintf(s, len, "%s %s %s", cp->ap->ad_cname.bv_val,
138                                                  tstr, cp->val.bv_val);
139                                 bv.bv_val = s;
140                                 rc = value_add_one( &c->rvalue_vals, &bv );
141                                 if (rc) return rc;
142                                 rc = value_add_one( &c->rvalue_nvals, &bv );
143                                 if (rc) return rc;
144                                 ch_free(s);
145                         }
146                         break;
147                 default:
148                         abort();
149                         break;
150                 }
151                 break;
152         case LDAP_MOD_DELETE:
153                 switch (c->type) {
154                 case CONSTRAINT_ATTRIBUTE:
155                         if (!cn) break; /* nothing to do */
156                                         
157                         if (c->valx < 0) {
158                                 /* zap all constraints */
159                                 while (cn) {
160                                         cp = cn->ap_next;
161                                         constraint_free( cn );
162                                         cn = cp;
163                                 }
164                                                 
165                                 on->on_bi.bi_private = NULL;
166                         } else {
167                                 constraint **cpp;
168                                                 
169                                 /* zap constraint numbered 'valx' */
170                                 for(i=0, cp = cn, cpp = &cn;
171                                         (cp) && (i<c->valx);
172                                         i++, cpp = &cp->ap_next, cp = *cpp);
173
174                                 if (cp) {
175                                         /* zap cp, and join cpp to cp->ap_next */
176                                         *cpp = cp->ap_next;
177                                         constraint_free( cp );
178                                 }
179                                 on->on_bi.bi_private = cn;
180                         }
181                         break;
182
183                 default:
184                         abort();
185                         break;
186                 }
187                 break;
188         case SLAP_CONFIG_ADD:
189         case LDAP_MOD_ADD:
190                 switch (c->type) {
191                 case CONSTRAINT_ATTRIBUTE:
192                         if ( slap_str2ad( c->argv[1], &ap.ap, &text ) ) {
193                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
194                                         "%s <%s>: %s\n", c->argv[0], c->argv[1], text );
195                                 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
196                                            "%s: %s\n", c->log, c->cr_msg, 0 );
197                                 return( ARG_BAD_CONF );
198                         }
199
200                         if ( strcasecmp( c->argv[2], REGEX_STR ) == 0) {
201                                 int err;
202                         
203                                 ap.re = ch_malloc( sizeof(regex_t) );
204                                 if ((err = regcomp( ap.re,
205                                         c->argv[3], REG_EXTENDED )) != 0) {
206                                         char errmsg[1024];
207                                                         
208                                         regerror( err, ap.re, errmsg, sizeof(errmsg) );
209                                         ch_free(ap.re);
210                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
211                                            "%s %s: Illegal regular expression \"%s\": Error %s",
212                                            c->argv[0], c->argv[1], c->argv[3], errmsg);
213                                         Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
214                                                 "%s: %s\n", c->log, c->cr_msg, 0 );
215                                         ap.re = NULL;
216                                         return( ARG_BAD_CONF );
217                                 }
218                                 ber_str2bv( c->argv[3], 0, 1, &ap.val );
219                         } else if ( strcasecmp( c->argv[2], URI_STR ) == 0) {
220                                 int err;
221                         
222                                 err = ldap_url_parse(c->argv[3], &ap.lud);
223                                 if ( err != LDAP_URL_SUCCESS ) {
224                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
225                                                 "%s %s: Invalid URI \"%s\"",
226                                                 c->argv[0], c->argv[1], c->argv[3]);
227                                         Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
228                                                 "%s: %s\n", c->log, c->cr_msg, 0 );
229                                         return( ARG_BAD_CONF );
230                                 }
231
232                                 if (ap.lud->lud_host != NULL) {
233                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
234                                                 "%s %s: unsupported hostname in URI \"%s\"",
235                                                 c->argv[0], c->argv[1], c->argv[3]);
236                                         Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
237                                                 "%s: %s\n", c->log, c->cr_msg, 0 );
238
239                                         ldap_free_urldesc(ap.lud);
240
241                                         return( ARG_BAD_CONF );
242                                 }
243
244                                 for ( i=0; ap.lud->lud_attrs[i]; i++);
245                                 /* FIXME: This is worthless without at least one attr */
246                                 if ( i ) {
247                                         ap.attrs = ch_malloc( (i+1)*sizeof(AttributeDescription *));
248                                         for ( i=0; ap.lud->lud_attrs[i]; i++) {
249                                                 ap.attrs[i] = NULL;
250                                                 if ( slap_str2ad( ap.lud->lud_attrs[i], &ap.attrs[i], &text ) ) {
251                                                         ch_free( ap.attrs );
252                                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
253                                                                 "%s <%s>: %s\n", c->argv[0], ap.lud->lud_attrs[i], text );
254                                                         Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
255                                                                    "%s: %s\n", c->log, c->cr_msg, 0 );
256                                                         return( ARG_BAD_CONF );
257                                                 }
258                                         }
259                                         ap.attrs[i] = NULL;
260                                 }
261
262                                 if (ap.lud->lud_dn == NULL)
263                                         ap.lud->lud_dn = ch_strdup("");
264
265                                 if (ap.lud->lud_filter == NULL)
266                                         ap.lud->lud_filter = ch_strdup("objectClass=*");
267
268                                 ber_str2bv( c->argv[3], 0, 1, &ap.val );
269                         } else {
270                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
271                                    "%s %s: Unknown constraint type: %s",
272                                    c->argv[0], c->argv[1], c->argv[2] );
273                                 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
274                                    "%s: %s\n", c->log, c->cr_msg, 0 );
275                                 return ( ARG_BAD_CONF );
276                         }
277
278                         a2 = ch_calloc( sizeof(constraint), 1 );
279                         a2->ap_next = on->on_bi.bi_private;
280                         a2->ap = ap.ap;
281                         a2->re = ap.re;
282                         a2->val = ap.val;
283                         a2->lud = ap.lud;
284                         if ( a2->lud ) {
285                                 ber_str2bv(a2->lud->lud_dn, 0, 0, &a2->dn);
286                                 ber_str2bv(a2->lud->lud_filter, 0, 0, &a2->filter);
287                         }
288                         a2->attrs = ap.attrs;
289                         on->on_bi.bi_private = a2;
290                         break;
291                 default:
292                         abort();
293                         break;
294                 }
295                 break;
296         default:
297                 abort();
298         }
299
300         return rc;
301 }
302
303 static int
304 constraint_uri_cb( Operation *op, SlapReply *rs ) 
305 {
306         if(rs->sr_type == REP_SEARCH) {
307                 int *foundp = op->o_callback->sc_private;
308
309                 *foundp = 1;
310
311                 Debug(LDAP_DEBUG_TRACE, "==> constraint_uri_cb <%s>\n",
312                         rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
313         }
314         return 0;
315 }
316
317 static int
318 constraint_violation( constraint *c, struct berval *bv, Operation *op, SlapReply *rs)
319 {
320         if ((!c) || (!bv)) return 0;
321         
322         if ((c->re) &&
323                 (regexec(c->re, bv->bv_val, 0, NULL, 0) == REG_NOMATCH))
324                 return 1; /* regular expression violation */
325
326         if (c->lud) {
327                 Operation nop = *op;
328                 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
329                 slap_callback cb;
330                 SlapReply nrs = { REP_RESULT };
331                 int i;
332                 int found;
333                 int rc;
334                 size_t len;
335                 struct berval filterstr;
336                 char *ptr;
337
338                 found = 0;
339
340                 nrs.sr_entry = NULL;
341                 nrs.sr_nentries = 0;
342
343                 cb.sc_next = NULL;
344                 cb.sc_response = constraint_uri_cb;
345                 cb.sc_cleanup = NULL;
346                 cb.sc_private = &found;
347
348                 nop.o_protocol = LDAP_VERSION3;
349                 nop.o_tag = LDAP_REQ_SEARCH;
350                 nop.o_time = slap_get_time();
351                 if (c->lud->lud_dn) {
352                         struct berval dn;
353
354                         ber_str2bv(c->lud->lud_dn, 0, 0, &dn);
355                         nop.o_req_dn = dn;
356                         nop.o_req_ndn = dn;
357                         nop.o_bd = select_backend(&nop.o_req_ndn, 1 );
358                         if (!nop.o_bd || !nop.o_bd->be_search) {
359                                 return 1; /* unexpected error */
360                         }
361                 } else {
362                         nop.o_req_dn = nop.o_bd->be_nsuffix[0];
363                         nop.o_req_ndn = nop.o_bd->be_nsuffix[0];
364                         nop.o_bd = on->on_info->oi_origdb;
365                 }
366                 nop.o_do_not_cache = 1;
367                 nop.o_callback = &cb;
368
369                 nop.ors_scope = c->lud->lud_scope;
370                 nop.ors_deref = LDAP_DEREF_NEVER;
371                 nop.ors_slimit = SLAP_NO_LIMIT;
372                 nop.ors_tlimit = SLAP_NO_LIMIT;
373                 nop.ors_limit = NULL;
374
375                 nop.ors_attrsonly = 0;
376                 nop.ors_attrs = slap_anlist_no_attrs;
377
378                 len = STRLENOF("(&(") + 
379                           c->filter.bv_len +
380                           STRLENOF(")(|");
381
382                 for (i = 0; c->attrs[i]; i++) {
383                         len += STRLENOF("(") +
384                                    c->attrs[i]->ad_cname.bv_len +
385                                    STRLENOF("=") + 
386                                    bv->bv_len +
387                                    STRLENOF(")");
388                 }
389
390                 len += STRLENOF("))");
391                 filterstr.bv_len = len;
392                 filterstr.bv_val = op->o_tmpalloc(len + 1, op->o_tmpmemctx);
393
394                 ptr = filterstr.bv_val +
395                         snprintf(filterstr.bv_val, len, "(&(%s)(|", c->lud->lud_filter);
396                 for (i = 0; c->attrs[i]; i++) {
397                         *ptr++ = '(';
398                         ptr = lutil_strcopy( ptr, c->attrs[i]->ad_cname.bv_val );
399                         *ptr++ = '=';
400                         ptr = lutil_strcopy( ptr, bv->bv_val );
401                         *ptr++ = ')';
402                 }
403                 *ptr++ = ')';
404                 *ptr++ = ')';
405
406                 Debug(LDAP_DEBUG_TRACE, 
407                         "==> constraint_violation uri filter = %s\n",
408                         filterstr.bv_val, 0, 0);
409
410                 nop.ors_filterstr = filterstr;
411                 nop.ors_filter = str2filter_x(&nop, filterstr.bv_val);
412
413                 rc = nop.o_bd->be_search( &nop, &nrs );
414                 
415                 op->o_tmpfree(filterstr.bv_val, op->o_tmpmemctx);
416                 Debug(LDAP_DEBUG_TRACE, 
417                         "==> constraint_violation uri rc = %d, found = %d\n",
418                         rc, found, 0);
419
420                 if((rc != LDAP_SUCCESS) && (rc != LDAP_NO_SUCH_OBJECT)) {
421                         send_ldap_error(op, rs, rc, 
422                                 "constraint_violation uri search failed");
423                         return 1; /* unexpected error */
424                 }
425
426                 if (!found)
427                         return 1; /* constraint violation */
428                         
429         }
430         
431         return 0;
432 }
433
434 static char *
435 print_message( struct berval *errtext, AttributeDescription *a )
436 {
437         char *ret;
438         int sz;
439         
440         sz = errtext->bv_len + sizeof(" on ") + a->ad_cname.bv_len;
441         ret = ch_malloc(sz);
442         snprintf( ret, sz, "%s on %s", errtext->bv_val, a->ad_cname.bv_val );
443         return ret;
444 }
445
446 static int
447 constraint_add( Operation *op, SlapReply *rs )
448 {
449         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
450         Attribute *a;
451         constraint *c = on->on_bi.bi_private, *cp;
452         BerVarray b = NULL;
453         int i;
454         struct berval rsv = BER_BVC("add breaks constraint");
455         char *msg;
456
457         if ((a = op->ora_e->e_attrs) == NULL) {
458                 op->o_bd->bd_info = (BackendInfo *)(on->on_info);
459                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
460                         "constraint_add() got null op.ora_e.e_attrs");
461                 return(rs->sr_err);
462         }
463
464         for(; a; a = a->a_next ) {
465                 /* we don't constrain operational attributes */
466                 if (is_at_operational(a->a_desc->ad_type)) continue;
467
468                 for(cp = c; cp; cp = cp->ap_next) {
469                         if (cp->ap != a->a_desc) continue;
470                         if ((b = a->a_vals) == NULL) continue;
471                                 
472                         for(i=0; b[i].bv_val; i++) {
473                                 int cv = constraint_violation( cp, &b[i], op, rs);
474                                         
475                                 if (cv) {
476                                         /* violation */
477                                         op->o_bd->bd_info = (BackendInfo *)(on->on_info);
478                                         msg = print_message( &rsv, a->a_desc );
479                                         send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, msg );
480                                         ch_free(msg);
481                                         return (rs->sr_err);
482                                 }
483                         }
484                 }
485         }
486         /* Default is to just fall through to the normal processing */
487         return SLAP_CB_CONTINUE;
488 }
489
490 static int
491 constraint_modify( Operation *op, SlapReply *rs )
492 {
493         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
494         constraint *c = on->on_bi.bi_private, *cp;
495         Modifications *m;
496         BerVarray b = NULL;
497         int i;
498         struct berval rsv = BER_BVC("modify breaks constraint");
499         char *msg;
500         
501         if ((m = op->orm_modlist) == NULL) {
502                 op->o_bd->bd_info = (BackendInfo *)(on->on_info);
503                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
504                                                 "constraint_modify() got null orm_modlist");
505                 return(rs->sr_err);
506         }
507
508         for(;m; m = m->sml_next) {
509                 if (is_at_operational( m->sml_desc->ad_type )) continue;
510                 if ((( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_ADD) &&
511                         (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_REPLACE))
512                         continue;
513                 /* we only care about ADD and REPLACE modifications */
514                 if ((( b = m->sml_values ) == NULL ) || (b[0].bv_val == NULL))
515                         continue;
516
517                 for(cp = c; cp; cp = cp->ap_next) {
518                         if (cp->ap != m->sml_desc) continue;
519                         
520                         for(i=0; b[i].bv_val; i++) {
521                                 int cv = constraint_violation( cp, &b[i], op, rs);
522                                 
523                                 if (cv) {
524                                         /* violation */
525                                         op->o_bd->bd_info = (BackendInfo *)(on->on_info);
526                                         msg = print_message( &rsv, m->sml_desc );
527                                         send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, msg );
528                                         ch_free(msg);
529                                         return (rs->sr_err);
530                                 }
531                         }
532                 }
533         }
534         
535         return SLAP_CB_CONTINUE;
536 }
537
538 static int
539 constraint_close(
540         BackendDB *be,
541         ConfigReply *cr )
542 {
543         slap_overinst *on = (slap_overinst *) be->bd_info;
544         constraint *ap, *a2;
545
546         for ( ap = on->on_bi.bi_private; ap; ap = a2 ) {
547                 a2 = ap->ap_next;
548                 constraint_free( ap );
549         }
550
551         return 0;
552 }
553
554 static slap_overinst constraint_ovl;
555
556 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC
557 static
558 #endif
559 int
560 constraint_initialize( void ) {
561         int rc;
562
563         constraint_ovl.on_bi.bi_type = "constraint";
564         constraint_ovl.on_bi.bi_db_close = constraint_close;
565         constraint_ovl.on_bi.bi_op_add = constraint_add;
566         constraint_ovl.on_bi.bi_op_modify = constraint_modify;
567
568         constraint_ovl.on_bi.bi_private = NULL;
569         
570         constraint_ovl.on_bi.bi_cf_ocs = constraintocs;
571         rc = config_register_schema( constraintcfg, constraintocs );
572         if (rc) return rc;
573         
574         return overlay_register( &constraint_ovl );
575 }
576
577 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC
578 int init_module(int argc, char *argv[]) {
579         return constraint_initialize();
580 }
581 #endif
582
583 #endif /* defined(SLAPD_OVER_CONSTRAINT) */
584