]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/constraint.c
Remove unused variable
[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_malloc( sizeof(constraint) );
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                         ber_str2bv(a2->lud->lud_dn, 0, 0, &a2->dn);
285                         ber_str2bv(a2->lud->lud_filter, 0, 0, &a2->filter);
286                         a2->attrs = ap.attrs;
287                         on->on_bi.bi_private = a2;
288                         break;
289                 default:
290                         abort();
291                         break;
292                 }
293                 break;
294         default:
295                 abort();
296         }
297
298         return rc;
299 }
300
301 static int
302 constraint_uri_cb( Operation *op, SlapReply *rs ) 
303 {
304         if(rs->sr_type == REP_SEARCH) {
305                 int *foundp = op->o_callback->sc_private;
306
307                 *foundp = 1;
308
309                 Debug(LDAP_DEBUG_TRACE, "==> constraint_uri_cb <%s>\n",
310                         rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
311         }
312         return 0;
313 }
314
315 static int
316 constraint_violation( constraint *c, struct berval *bv, Operation *op, SlapReply *rs)
317 {
318         if ((!c) || (!bv)) return 0;
319         
320         if ((c->re) &&
321                 (regexec(c->re, bv->bv_val, 0, NULL, 0) == REG_NOMATCH))
322                 return 1; /* regular expression violation */
323
324         if (c->lud) {
325                 Operation nop = *op;
326                 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
327                 slap_callback cb;
328                 SlapReply nrs = { REP_RESULT };
329                 int i;
330                 int found;
331                 int rc;
332                 size_t len;
333                 struct berval filterstr;
334                 char *ptr;
335
336                 found = 0;
337
338                 nrs.sr_entry = NULL;
339                 nrs.sr_nentries = 0;
340
341                 cb.sc_next = NULL;
342                 cb.sc_response = constraint_uri_cb;
343                 cb.sc_cleanup = NULL;
344                 cb.sc_private = &found;
345
346                 nop.o_protocol = LDAP_VERSION3;
347                 nop.o_tag = LDAP_REQ_SEARCH;
348                 nop.o_time = slap_get_time();
349                 if (c->lud->lud_dn) {
350                         struct berval dn;
351
352                         ber_str2bv(c->lud->lud_dn, 0, 0, &dn);
353                         nop.o_req_dn = dn;
354                         nop.o_req_ndn = dn;
355                         nop.o_bd = select_backend(&nop.o_req_ndn, 1 );
356                         if (!nop.o_bd || !nop.o_bd->be_search) {
357                                 return 1; /* unexpected error */
358                         }
359                 } else {
360                         nop.o_req_dn = nop.o_bd->be_nsuffix[0];
361                         nop.o_req_ndn = nop.o_bd->be_nsuffix[0];
362                         nop.o_bd = on->on_info->oi_origdb;
363                 }
364                 nop.o_do_not_cache = 1;
365                 nop.o_callback = &cb;
366
367                 nop.ors_scope = c->lud->lud_scope;
368                 nop.ors_deref = LDAP_DEREF_NEVER;
369                 nop.ors_slimit = SLAP_NO_LIMIT;
370                 nop.ors_tlimit = SLAP_NO_LIMIT;
371                 nop.ors_limit = NULL;
372
373                 nop.ors_attrsonly = 0;
374                 nop.ors_attrs = slap_anlist_no_attrs;
375
376                 len = STRLENOF("(&(") + 
377                           c->filter.bv_len +
378                           STRLENOF(")(|");
379
380                 for (i = 0; c->attrs[i]; i++) {
381                         len += STRLENOF("(") +
382                                    c->attrs[i]->ad_cname.bv_len +
383                                    STRLENOF("=") + 
384                                    bv->bv_len +
385                                    STRLENOF(")");
386                 }
387
388                 len += STRLENOF("))");
389                 filterstr.bv_len = len;
390                 filterstr.bv_val = op->o_tmpalloc(len + 1, op->o_tmpmemctx);
391
392                 ptr = filterstr.bv_val +
393                         snprintf(filterstr.bv_val, len, "(&(%s)(|", c->lud->lud_filter);
394                 for (i = 0; c->attrs[i]; i++) {
395                         *ptr++ = '(';
396                         ptr = lutil_strcopy( ptr, c->attrs[i]->ad_cname.bv_val );
397                         *ptr++ = '=';
398                         ptr = lutil_strcopy( ptr, bv->bv_val );
399                         *ptr++ = ')';
400                 }
401                 *ptr++ = ')';
402                 *ptr++ = ')';
403
404                 Debug(LDAP_DEBUG_TRACE, 
405                         "==> constraint_violation uri filter = %s\n",
406                         filterstr.bv_val, 0, 0);
407
408                 nop.ors_filterstr = filterstr;
409                 nop.ors_filter = str2filter_x(&nop, filterstr.bv_val);
410
411                 rc = nop.o_bd->be_search( &nop, &nrs );
412                 
413                 op->o_tmpfree(filterstr.bv_val, op->o_tmpmemctx);
414                 Debug(LDAP_DEBUG_TRACE, 
415                         "==> constraint_violation uri rc = %d, found = %d\n",
416                         rc, found, 0);
417
418                 if((rc != LDAP_SUCCESS) && (rc != LDAP_NO_SUCH_OBJECT)) {
419                         send_ldap_error(op, rs, rc, 
420                                 "constraint_violation uri search failed");
421                         return 1; /* unexpected error */
422                 }
423
424                 if (!found)
425                         return 1; /* constraint violation */
426                         
427         }
428         
429         return 0;
430 }
431
432 static char *
433 print_message( struct berval *errtext, AttributeDescription *a )
434 {
435         char *ret;
436         int sz;
437         
438         sz = errtext->bv_len + sizeof(" on ") + a->ad_cname.bv_len;
439         ret = ch_malloc(sz);
440         snprintf( ret, sz, "%s on %s", errtext->bv_val, a->ad_cname.bv_val );
441         return ret;
442 }
443
444 static int
445 constraint_add( Operation *op, SlapReply *rs )
446 {
447         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
448         Attribute *a;
449         constraint *c = on->on_bi.bi_private, *cp;
450         BerVarray b = NULL;
451         int i;
452         struct berval rsv = BER_BVC("add breaks constraint");
453         char *msg;
454
455         if ((a = op->ora_e->e_attrs) == NULL) {
456                 op->o_bd->bd_info = (BackendInfo *)(on->on_info);
457                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
458                         "constraint_add() got null op.ora_e.e_attrs");
459                 return(rs->sr_err);
460         }
461
462         for(; a; a = a->a_next ) {
463                 /* we don't constrain operational attributes */
464                 if (is_at_operational(a->a_desc->ad_type)) continue;
465
466                 for(cp = c; cp; cp = cp->ap_next) {
467                         if (cp->ap != a->a_desc) continue;
468                         if ((b = a->a_vals) == NULL) continue;
469                                 
470                         for(i=0; b[i].bv_val; i++) {
471                                 int cv = constraint_violation( cp, &b[i], op, rs);
472                                         
473                                 if (cv) {
474                                         /* violation */
475                                         op->o_bd->bd_info = (BackendInfo *)(on->on_info);
476                                         msg = print_message( &rsv, a->a_desc );
477                                         send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, msg );
478                                         ch_free(msg);
479                                         return (rs->sr_err);
480                                 }
481                         }
482                 }
483         }
484         /* Default is to just fall through to the normal processing */
485         return SLAP_CB_CONTINUE;
486 }
487
488 static int
489 constraint_modify( Operation *op, SlapReply *rs )
490 {
491         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
492         constraint *c = on->on_bi.bi_private, *cp;
493         Modifications *m;
494         BerVarray b = NULL;
495         int i;
496         struct berval rsv = BER_BVC("modify breaks constraint");
497         char *msg;
498         
499         if ((m = op->orm_modlist) == NULL) {
500                 op->o_bd->bd_info = (BackendInfo *)(on->on_info);
501                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
502                                                 "constraint_modify() got null orm_modlist");
503                 return(rs->sr_err);
504         }
505
506         for(;m; m = m->sml_next) {
507                 if (is_at_operational( m->sml_desc->ad_type )) continue;
508                 if ((( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_ADD) &&
509                         (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_REPLACE))
510                         continue;
511                 /* we only care about ADD and REPLACE modifications */
512                 if ((( b = m->sml_values ) == NULL ) || (b[0].bv_val == NULL))
513                         continue;
514
515                 for(cp = c; cp; cp = cp->ap_next) {
516                         if (cp->ap != m->sml_desc) continue;
517                         
518                         for(i=0; b[i].bv_val; i++) {
519                                 int cv = constraint_violation( cp, &b[i], op, rs);
520                                 
521                                 if (cv) {
522                                         /* violation */
523                                         op->o_bd->bd_info = (BackendInfo *)(on->on_info);
524                                         msg = print_message( &rsv, m->sml_desc );
525                                         send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, msg );
526                                         ch_free(msg);
527                                         return (rs->sr_err);
528                                 }
529                         }
530                 }
531         }
532         
533         return SLAP_CB_CONTINUE;
534 }
535
536 static int
537 constraint_close(
538         BackendDB *be,
539         ConfigReply *cr )
540 {
541         slap_overinst *on = (slap_overinst *) be->bd_info;
542         constraint *ap, *a2;
543
544         for ( ap = on->on_bi.bi_private; ap; ap = a2 ) {
545                 a2 = ap->ap_next;
546                 constraint_free( ap );
547         }
548
549         return 0;
550 }
551
552 static slap_overinst constraint_ovl;
553
554 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC
555 static
556 #endif
557 int
558 constraint_initialize( void ) {
559         int rc;
560
561         constraint_ovl.on_bi.bi_type = "constraint";
562         constraint_ovl.on_bi.bi_db_close = constraint_close;
563         constraint_ovl.on_bi.bi_op_add = constraint_add;
564         constraint_ovl.on_bi.bi_op_modify = constraint_modify;
565
566         constraint_ovl.on_bi.bi_private = NULL;
567         
568         constraint_ovl.on_bi.bi_cf_ocs = constraintocs;
569         rc = config_register_schema( constraintcfg, constraintocs );
570         if (rc) return rc;
571         
572         return overlay_register( &constraint_ovl );
573 }
574
575 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC
576 int init_module(int argc, char *argv[]) {
577         return constraint_initialize();
578 }
579 #endif
580
581 #endif /* defined(SLAPD_OVER_CONSTRAINT) */
582