Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / openldap / dist / servers / slapd / aci.c
blobac10029d445b8d4084502bed14ca4c17791b222e
1 /* aci.c - routines to parse and check acl's */
2 /* $OpenLDAP: pkg/ldap/servers/slapd/aci.c,v 1.14.2.6 2008/02/11 23:26:43 kurt Exp $ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2008 The OpenLDAP Foundation.
6 * All rights reserved.
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.
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>.
16 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
17 * All rights reserved.
19 * Redistribution and use in source and binary forms are permitted
20 * provided that this notice is preserved and that due credit is given
21 * to the University of Michigan at Ann Arbor. The name of the University
22 * may not be used to endorse or promote products derived from this
23 * software without specific prior written permission. This software
24 * is provided ``as is'' without express or implied warranty.
27 #include "portable.h"
29 #ifdef SLAPD_ACI_ENABLED
31 #include <stdio.h>
33 #include <ac/ctype.h>
34 #include <ac/regex.h>
35 #include <ac/socket.h>
36 #include <ac/string.h>
37 #include <ac/unistd.h>
39 #include "slap.h"
40 #include "lber_pvt.h"
41 #include "lutil.h"
43 /* use most appropriate size */
44 #define ACI_BUF_SIZE 1024
46 /* move to "stable" when no longer experimental */
47 #define SLAPD_ACI_SYNTAX "1.3.6.1.4.1.4203.666.2.1"
49 /* change this to "OpenLDAPset" */
50 #define SLAPD_ACI_SET_ATTR "template"
52 typedef enum slap_aci_scope_t {
53 SLAP_ACI_SCOPE_ENTRY = 0x1,
54 SLAP_ACI_SCOPE_CHILDREN = 0x2,
55 SLAP_ACI_SCOPE_SUBTREE = ( SLAP_ACI_SCOPE_ENTRY | SLAP_ACI_SCOPE_CHILDREN )
56 } slap_aci_scope_t;
58 enum {
59 ACI_BV_ENTRY,
60 ACI_BV_CHILDREN,
61 ACI_BV_ONELEVEL,
62 ACI_BV_SUBTREE,
64 ACI_BV_BR_ENTRY,
65 ACI_BV_BR_CHILDREN,
66 ACI_BV_BR_ALL,
68 ACI_BV_ACCESS_ID,
69 ACI_BV_PUBLIC,
70 ACI_BV_USERS,
71 ACI_BV_SELF,
72 ACI_BV_DNATTR,
73 ACI_BV_GROUP,
74 ACI_BV_ROLE,
75 ACI_BV_SET,
76 ACI_BV_SET_REF,
78 ACI_BV_GRANT,
79 ACI_BV_DENY,
81 ACI_BV_GROUP_CLASS,
82 ACI_BV_GROUP_ATTR,
83 ACI_BV_ROLE_CLASS,
84 ACI_BV_ROLE_ATTR,
86 ACI_BV_SET_ATTR,
88 ACI_BV_LAST
91 static const struct berval aci_bv[] = {
92 /* scope */
93 BER_BVC("entry"),
94 BER_BVC("children"),
95 BER_BVC("onelevel"),
96 BER_BVC("subtree"),
98 /* */
99 BER_BVC("[entry]"),
100 BER_BVC("[children]"),
101 BER_BVC("[all]"),
103 /* type */
104 BER_BVC("access-id"),
105 BER_BVC("public"),
106 BER_BVC("users"),
107 BER_BVC("self"),
108 BER_BVC("dnattr"),
109 BER_BVC("group"),
110 BER_BVC("role"),
111 BER_BVC("set"),
112 BER_BVC("set-ref"),
114 /* actions */
115 BER_BVC("grant"),
116 BER_BVC("deny"),
118 /* schema */
119 BER_BVC(SLAPD_GROUP_CLASS),
120 BER_BVC(SLAPD_GROUP_ATTR),
121 BER_BVC(SLAPD_ROLE_CLASS),
122 BER_BVC(SLAPD_ROLE_ATTR),
124 BER_BVC(SLAPD_ACI_SET_ATTR),
126 BER_BVNULL
129 static AttributeDescription *slap_ad_aci;
131 static int
132 OpenLDAPaciValidate(
133 Syntax *syntax,
134 struct berval *val );
136 static int
137 OpenLDAPaciPretty(
138 Syntax *syntax,
139 struct berval *val,
140 struct berval *out,
141 void *ctx );
143 static int
144 OpenLDAPaciNormalize(
145 slap_mask_t use,
146 Syntax *syntax,
147 MatchingRule *mr,
148 struct berval *val,
149 struct berval *out,
150 void *ctx );
152 #define OpenLDAPaciMatch octetStringMatch
154 static int
155 aci_list_map_rights(
156 struct berval *list )
158 struct berval bv;
159 slap_access_t mask;
160 int i;
162 ACL_INIT( mask );
163 for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
164 if ( bv.bv_len <= 0 ) {
165 continue;
168 switch ( *bv.bv_val ) {
169 case 'x':
170 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not
171 * define any equivalent to the AUTH right, so I've just used
172 * 'x' for now.
174 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
175 break;
176 case 'd':
177 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
178 * the right 'd' to mean "delete"; we hijack it to mean
179 * "disclose" for consistency wuith the rest of slapd.
181 ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
182 break;
183 case 'c':
184 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
185 break;
186 case 's':
187 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
188 * the right 's' to mean "set", but in the examples states
189 * that the right 's' means "search". The latter definition
190 * is used here.
192 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
193 break;
194 case 'r':
195 ACL_PRIV_SET(mask, ACL_PRIV_READ);
196 break;
197 case 'w':
198 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
199 break;
200 default:
201 break;
206 return mask;
209 static int
210 aci_list_has_attr(
211 struct berval *list,
212 const struct berval *attr,
213 struct berval *val )
215 struct berval bv, left, right;
216 int i;
218 for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
219 if ( acl_get_part(&bv, 0, '=', &left ) < 0
220 || acl_get_part( &bv, 1, '=', &right ) < 0 )
222 if ( ber_bvstrcasecmp( attr, &bv ) == 0 ) {
223 return(1);
226 } else if ( val == NULL ) {
227 if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
228 return(1);
231 } else {
232 if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
233 /* FIXME: this is also totally undocumented! */
234 /* this is experimental code that implements a
235 * simple (prefix) match of the attribute value.
236 * the ACI draft does not provide for aci's that
237 * apply to specific values, but it would be
238 * nice to have. If the <attr> part of an aci's
239 * rights list is of the form <attr>=<value>,
240 * that means the aci applies only to attrs with
241 * the given value. Furthermore, if the attr is
242 * of the form <attr>=<value>*, then <value> is
243 * treated as a prefix, and the aci applies to
244 * any value with that prefix.
246 * Ideally, this would allow r.e. matches.
248 if ( acl_get_part( &right, 0, '*', &left ) < 0
249 || right.bv_len <= left.bv_len )
251 if ( ber_bvstrcasecmp( val, &right ) == 0 ) {
252 return 1;
255 } else if ( val->bv_len >= left.bv_len ) {
256 if ( strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0 ) {
257 return(1);
264 return 0;
267 static slap_access_t
268 aci_list_get_attr_rights(
269 struct berval *list,
270 const struct berval *attr,
271 struct berval *val )
273 struct berval bv;
274 slap_access_t mask;
275 int i;
277 /* loop through each rights/attr pair, skip first part (action) */
278 ACL_INIT(mask);
279 for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) {
280 if ( aci_list_has_attr( &bv, attr, val ) == 0 ) {
281 Debug( LDAP_DEBUG_ACL,
282 " <= aci_list_get_attr_rights "
283 "test %s for %s -> failed\n",
284 bv.bv_val, attr->bv_val, 0 );
285 continue;
288 Debug( LDAP_DEBUG_ACL,
289 " <= aci_list_get_attr_rights "
290 "test %s for %s -> ok\n",
291 bv.bv_val, attr->bv_val, 0 );
293 if ( acl_get_part( list, i, ';', &bv ) < 0 ) {
294 Debug( LDAP_DEBUG_ACL,
295 " <= aci_list_get_attr_rights "
296 "test no rights\n",
297 0, 0, 0 );
298 continue;
301 mask |= aci_list_map_rights( &bv );
302 Debug( LDAP_DEBUG_ACL,
303 " <= aci_list_get_attr_rights "
304 "rights %s to mask 0x%x\n",
305 bv.bv_val, mask, 0 );
308 return mask;
311 static int
312 aci_list_get_rights(
313 struct berval *list,
314 struct berval *attr,
315 struct berval *val,
316 slap_access_t *grant,
317 slap_access_t *deny )
319 struct berval perm, actn, baseattr;
320 slap_access_t *mask;
321 int i, found;
323 if ( attr == NULL || BER_BVISEMPTY( attr ) ) {
324 attr = (struct berval *)&aci_bv[ ACI_BV_ENTRY ];
326 } else if ( acl_get_part( attr, 0, ';', &baseattr ) > 0 ) {
327 attr = &baseattr;
329 found = 0;
330 ACL_INIT(*grant);
331 ACL_INIT(*deny);
332 /* loop through each permissions clause */
333 for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) {
334 if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) {
335 continue;
338 if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) {
339 mask = grant;
341 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DENY ], &actn ) == 0 ) {
342 mask = deny;
344 } else {
345 continue;
348 found = 1;
349 *mask |= aci_list_get_attr_rights( &perm, attr, val );
350 *mask |= aci_list_get_attr_rights( &perm, &aci_bv[ ACI_BV_BR_ALL ], NULL );
353 return found;
356 static int
357 aci_group_member (
358 struct berval *subj,
359 const struct berval *defgrpoc,
360 const struct berval *defgrpat,
361 Operation *op,
362 Entry *e,
363 int nmatch,
364 regmatch_t *matches
367 struct berval subjdn;
368 struct berval grpoc;
369 struct berval grpat;
370 ObjectClass *grp_oc = NULL;
371 AttributeDescription *grp_ad = NULL;
372 const char *text;
373 int rc;
375 /* format of string is "{group|role}/objectClassValue/groupAttrName" */
376 if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
377 return 0;
380 if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) {
381 grpoc = *defgrpoc;
384 if ( acl_get_part( subj, 2, '/', &grpat ) < 0 ) {
385 grpat = *defgrpat;
388 rc = slap_bv2ad( &grpat, &grp_ad, &text );
389 if ( rc != LDAP_SUCCESS ) {
390 rc = 0;
391 goto done;
393 rc = 0;
395 grp_oc = oc_bvfind( &grpoc );
397 if ( grp_oc != NULL && grp_ad != NULL ) {
398 char buf[ ACI_BUF_SIZE ];
399 struct berval bv, ndn;
401 bv.bv_len = sizeof( buf ) - 1;
402 bv.bv_val = (char *)&buf;
403 if ( acl_string_expand( &bv, &subjdn,
404 e->e_ndn, nmatch, matches ) )
406 rc = LDAP_OTHER;
407 goto done;
410 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
412 rc = ( backend_group( op, e, &ndn, &op->o_ndn,
413 grp_oc, grp_ad ) == 0 );
414 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
418 done:
419 return rc;
422 static int
423 aci_mask(
424 Operation *op,
425 Entry *e,
426 AttributeDescription *desc,
427 struct berval *val,
428 struct berval *aci,
429 int nmatch,
430 regmatch_t *matches,
431 slap_access_t *grant,
432 slap_access_t *deny,
433 slap_aci_scope_t asserted_scope )
435 struct berval bv,
436 scope,
437 perms,
438 type,
439 opts,
440 sdn;
441 int rc;
444 assert( !BER_BVISNULL( &desc->ad_cname ) );
446 /* parse an aci of the form:
447 oid # scope # action;rights;attr;rights;attr
448 $ action;rights;attr;rights;attr # type # subject
450 [NOTE: the following comment is very outdated,
451 as the draft version it refers to (Ando, 2004-11-20)].
453 See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
454 a full description of the format for this attribute.
455 Differences: "this" in the draft is "self" here, and
456 "self" and "public" is in the position of type.
458 <scope> = {entry|children|subtree}
459 <type> = {public|users|access-id|subtree|onelevel|children|
460 self|dnattr|group|role|set|set-ref}
462 This routine now supports scope={ENTRY,CHILDREN}
463 with the semantics:
464 - ENTRY applies to "entry" and "subtree";
465 - CHILDREN applies to "children" and "subtree"
468 /* check that the aci has all 5 components */
469 if ( acl_get_part( aci, 4, '#', NULL ) < 0 ) {
470 return 0;
473 /* check that the aci family is supported */
474 /* FIXME: the OID is ignored? */
475 if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) {
476 return 0;
479 /* check that the scope matches */
480 if ( acl_get_part( aci, 1, '#', &scope ) < 0 ) {
481 return 0;
484 /* note: scope can be either ENTRY or CHILDREN;
485 * they respectively match "entry" and "children" in bv
486 * both match "subtree" */
487 switch ( asserted_scope ) {
488 case SLAP_ACI_SCOPE_ENTRY:
489 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
490 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
492 return 0;
494 break;
496 case SLAP_ACI_SCOPE_CHILDREN:
497 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
498 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
500 return 0;
502 break;
504 case SLAP_ACI_SCOPE_SUBTREE:
505 /* TODO: add assertion? */
506 return 0;
509 /* get the list of permissions clauses, bail if empty */
510 if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) {
511 assert( 0 );
512 return 0;
515 /* check if any permissions allow desired access */
516 if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
517 return 0;
520 /* see if we have a DN match */
521 if ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
522 assert( 0 );
523 return 0;
526 /* see if we have a public (i.e. anonymous) access */
527 if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
528 return 1;
531 /* otherwise require an identity */
532 if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
533 return 0;
536 /* see if we have a users access */
537 if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
538 return 1;
541 /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
542 * just grab all the berval up to its end (ITS#3303).
543 * NOTE: the problem could be solved by providing the DN with
544 * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would
545 * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
546 #if 0
547 if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) {
548 return 0;
550 #endif
551 sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
552 sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
554 /* get the type options, if any */
555 if ( acl_get_part( &type, 1, '/', &opts ) > 0 ) {
556 opts.bv_len = type.bv_len - ( opts.bv_val - type.bv_val );
557 type.bv_len = opts.bv_val - type.bv_val - 1;
559 } else {
560 BER_BVZERO( &opts );
563 if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
564 return dn_match( &op->o_ndn, &sdn );
566 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
567 return dnIsSuffix( &op->o_ndn, &sdn );
569 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
570 struct berval pdn;
572 dnParent( &sdn, &pdn );
574 return dn_match( &op->o_ndn, &pdn );
576 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
577 return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) );
579 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
580 return dn_match( &op->o_ndn, &e->e_nname );
582 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
583 Attribute *at;
584 AttributeDescription *ad = NULL;
585 const char *text;
587 rc = slap_bv2ad( &sdn, &ad, &text );
588 assert( rc == LDAP_SUCCESS );
590 rc = 0;
591 for ( at = attrs_find( e->e_attrs, ad );
592 at != NULL;
593 at = attrs_find( at->a_next, ad ) )
595 if ( attr_valfind( at,
596 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
597 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
598 &op->o_ndn, NULL, op->o_tmpmemctx ) == 0 )
600 rc = 1;
601 break;
605 return rc;
607 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
608 struct berval oc,
611 if ( BER_BVISNULL( &opts ) ) {
612 oc = aci_bv[ ACI_BV_GROUP_CLASS ];
613 at = aci_bv[ ACI_BV_GROUP_ATTR ];
615 } else {
616 if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
617 assert( 0 );
620 if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
621 at = aci_bv[ ACI_BV_GROUP_ATTR ];
625 if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
627 return 1;
630 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
631 struct berval oc,
634 if ( BER_BVISNULL( &opts ) ) {
635 oc = aci_bv[ ACI_BV_ROLE_CLASS ];
636 at = aci_bv[ ACI_BV_ROLE_ATTR ];
638 } else {
639 if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
640 assert( 0 );
643 if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
644 at = aci_bv[ ACI_BV_ROLE_ATTR ];
648 if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
650 return 1;
653 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
654 if ( acl_match_set( &sdn, op, e, NULL ) ) {
655 return 1;
658 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
659 if ( acl_match_set( &sdn, op, e, (struct berval *)&aci_bv[ ACI_BV_SET_ATTR ] ) ) {
660 return 1;
663 } else {
664 /* it passed normalization! */
665 assert( 0 );
668 return 0;
671 static int
672 aci_init( void )
674 /* OpenLDAP eXperimental Syntax */
675 static slap_syntax_defs_rec aci_syntax_def = {
676 "( 1.3.6.1.4.1.4203.666.2.1 DESC 'OpenLDAP Experimental ACI' )",
677 SLAP_SYNTAX_HIDE,
678 NULL,
679 OpenLDAPaciValidate,
680 OpenLDAPaciPretty
682 static slap_mrule_defs_rec aci_mr_def = {
683 "( 1.3.6.1.4.1.4203.666.4.2 NAME 'OpenLDAPaciMatch' "
684 "SYNTAX 1.3.6.1.4.1.4203.666.2.1 )",
685 SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
686 NULL, OpenLDAPaciNormalize, OpenLDAPaciMatch,
687 NULL, NULL,
688 NULL
690 static struct {
691 char *name;
692 char *desc;
693 slap_mask_t flags;
694 AttributeDescription **ad;
695 } aci_at = {
696 "OpenLDAPaci", "( 1.3.6.1.4.1.4203.666.1.5 "
697 "NAME 'OpenLDAPaci' "
698 "DESC 'OpenLDAP access control information (experimental)' "
699 "EQUALITY OpenLDAPaciMatch "
700 "SYNTAX 1.3.6.1.4.1.4203.666.2.1 "
701 "USAGE directoryOperation )",
702 SLAP_AT_HIDE,
703 &slap_ad_aci
706 int rc;
708 /* ACI syntax */
709 rc = register_syntax( &aci_syntax_def );
710 if ( rc != 0 ) {
711 return rc;
714 /* ACI equality rule */
715 rc = register_matching_rule( &aci_mr_def );
716 if ( rc != 0 ) {
717 return rc;
720 /* ACI attribute */
721 rc = register_at( aci_at.desc, aci_at.ad, 0 );
722 if ( rc != LDAP_SUCCESS ) {
723 Debug( LDAP_DEBUG_ANY,
724 "aci_init: at_register failed\n", 0, 0, 0 );
725 return rc;
728 /* install flags */
729 (*aci_at.ad)->ad_type->sat_flags |= aci_at.flags;
731 return rc;
734 static int
735 dynacl_aci_parse(
736 const char *fname,
737 int lineno,
738 const char *opts,
739 slap_style_t sty,
740 const char *right,
741 void **privp )
743 AttributeDescription *ad = NULL;
744 const char *text = NULL;
746 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
747 fprintf( stderr, "%s: line %d: "
748 "inappropriate style \"%s\" in \"aci\" by clause\n",
749 fname, lineno, style_strings[sty] );
750 return -1;
753 if ( right != NULL && *right != '\0' ) {
754 if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
755 fprintf( stderr,
756 "%s: line %d: aci \"%s\": %s\n",
757 fname, lineno, right, text );
758 return -1;
761 } else {
762 ad = slap_ad_aci;
765 if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
766 fprintf( stderr, "%s: line %d: "
767 "aci \"%s\": inappropriate syntax: %s\n",
768 fname, lineno, right,
769 ad->ad_type->sat_syntax_oid );
770 return -1;
773 *privp = (void *)ad;
775 return 0;
778 static int
779 dynacl_aci_unparse( void *priv, struct berval *bv )
781 AttributeDescription *ad = ( AttributeDescription * )priv;
782 char *ptr;
784 assert( ad != NULL );
786 bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
787 ptr = lutil_strcopy( bv->bv_val, " aci=" );
788 ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
789 bv->bv_len = ptr - bv->bv_val;
791 return 0;
794 static int
795 dynacl_aci_mask(
796 void *priv,
797 Operation *op,
798 Entry *e,
799 AttributeDescription *desc,
800 struct berval *val,
801 int nmatch,
802 regmatch_t *matches,
803 slap_access_t *grantp,
804 slap_access_t *denyp )
806 AttributeDescription *ad = ( AttributeDescription * )priv;
807 Attribute *at;
808 slap_access_t tgrant, tdeny, grant, deny;
809 #ifdef LDAP_DEBUG
810 char accessmaskbuf[ACCESSMASK_MAXLEN];
811 char accessmaskbuf1[ACCESSMASK_MAXLEN];
812 #endif /* LDAP_DEBUG */
814 if ( BER_BVISEMPTY( &e->e_nname ) ) {
815 /* no ACIs in the root DSE */
816 return -1;
819 /* start out with nothing granted, nothing denied */
820 ACL_INIT(tgrant);
821 ACL_INIT(tdeny);
823 /* get the aci attribute */
824 at = attr_find( e->e_attrs, ad );
825 if ( at != NULL ) {
826 int i;
828 /* the aci is an multi-valued attribute. The
829 * rights are determined by OR'ing the individual
830 * rights given by the acis.
832 for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
833 if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
834 nmatch, matches, &grant, &deny,
835 SLAP_ACI_SCOPE_ENTRY ) != 0 )
837 tgrant |= grant;
838 tdeny |= deny;
842 Debug( LDAP_DEBUG_ACL, " <= aci_mask grant %s deny %s\n",
843 accessmask2str( tgrant, accessmaskbuf, 1 ),
844 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
847 /* If the entry level aci didn't contain anything valid for the
848 * current operation, climb up the tree and evaluate the
849 * acis with scope set to subtree
851 if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
852 struct berval parent_ndn;
854 dnParent( &e->e_nname, &parent_ndn );
855 while ( !BER_BVISEMPTY( &parent_ndn ) ){
856 int i;
857 BerVarray bvals = NULL;
858 int ret, stop;
860 /* to solve the chicken'n'egg problem of accessing
861 * the OpenLDAPaci attribute, the direct access
862 * to the entry's attribute is unchecked; however,
863 * further accesses to OpenLDAPaci values in the
864 * ancestors occur through backend_attribute(), i.e.
865 * with the identity of the operation, requiring
866 * further access checking. For uniformity, this
867 * makes further requests occur as the rootdn, if
868 * any, i.e. searching for the OpenLDAPaci attribute
869 * is considered an internal search. If this is not
870 * acceptable, then the same check needs be performed
871 * when accessing the entry's attribute. */
872 struct berval save_o_dn, save_o_ndn;
874 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
875 save_o_dn = op->o_dn;
876 save_o_ndn = op->o_ndn;
878 op->o_dn = op->o_bd->be_rootdn;
879 op->o_ndn = op->o_bd->be_rootndn;
882 Debug( LDAP_DEBUG_ACL, " checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
883 ret = backend_attribute( op, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
885 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
886 op->o_dn = save_o_dn;
887 op->o_ndn = save_o_ndn;
890 switch ( ret ) {
891 case LDAP_SUCCESS :
892 stop = 0;
893 if ( !bvals ) {
894 break;
897 for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
898 if ( aci_mask( op, e, desc, val,
899 &bvals[i],
900 nmatch, matches,
901 &grant, &deny,
902 SLAP_ACI_SCOPE_CHILDREN ) != 0 )
904 tgrant |= grant;
905 tdeny |= deny;
906 /* evaluation stops as soon as either a "deny" or a
907 * "grant" directive matches.
909 if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
910 stop = 1;
913 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
914 accessmask2str( tgrant, accessmaskbuf, 1 ),
915 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
917 break;
919 case LDAP_NO_SUCH_ATTRIBUTE:
920 /* just go on if the aci-Attribute is not present in
921 * the current entry
923 Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 );
924 stop = 0;
925 break;
927 case LDAP_NO_SUCH_OBJECT:
928 /* We have reached the base object */
929 Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 );
930 stop = 1;
931 break;
933 default:
934 stop = 1;
935 break;
938 if ( stop ) {
939 break;
941 dnParent( &parent_ndn, &parent_ndn );
945 *grantp = tgrant;
946 *denyp = tdeny;
948 return 0;
951 /* need to register this at some point */
952 static slap_dynacl_t dynacl_aci = {
953 "aci",
954 dynacl_aci_parse,
955 dynacl_aci_unparse,
956 dynacl_aci_mask,
957 NULL,
958 NULL,
959 NULL
963 dynacl_aci_init( void )
965 int rc;
967 rc = aci_init();
969 if ( rc == 0 ) {
970 rc = slap_dynacl_register( &dynacl_aci );
973 return rc;
977 /* ACI syntax validation */
980 * Matches given berval to array of bervals
981 * Returns:
982 * >=0 if one if the array elements equals to this berval
983 * -1 if string was not found in array
985 static int
986 bv_getcaseidx(
987 struct berval *bv,
988 const struct berval *arr[] )
990 int i;
992 if ( BER_BVISEMPTY( bv ) ) {
993 return -1;
996 for ( i = 0; arr[ i ] != NULL ; i++ ) {
997 if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) {
998 return i;
1002 return -1;
1006 /* Returns what have left in input berval after current sub */
1007 static void
1008 bv_get_tail(
1009 struct berval *val,
1010 struct berval *sub,
1011 struct berval *tail )
1013 int head_len;
1015 tail->bv_val = sub->bv_val + sub->bv_len;
1016 head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val;
1017 tail->bv_len = val->bv_len - head_len;
1022 * aci is accepted in following form:
1023 * oid#scope#rights#type#subject
1024 * Where:
1025 * oid := numeric OID (currently ignored)
1026 * scope := entry|children|subtree
1027 * rights := right[[$right]...]
1028 * right := (grant|deny);action
1029 * action := perms;attrs[[;perms;attrs]...]
1030 * perms := perm[[,perm]...]
1031 * perm := c|s|r|w|x
1032 * attrs := attribute[[,attribute]..]|"[all]"
1033 * attribute := attributeType|attributeType=attributeValue|attributeType=attributeValuePrefix*
1034 * type := public|users|self|dnattr|group|role|set|set-ref|
1035 * access_id|subtree|onelevel|children
1037 static int
1038 OpenLDAPaciValidatePerms(
1039 struct berval *perms )
1041 int i;
1043 for ( i = 0; i < perms->bv_len; ) {
1044 switch ( perms->bv_val[ i ] ) {
1045 case 'x':
1046 case 'd':
1047 case 'c':
1048 case 's':
1049 case 'r':
1050 case 'w':
1051 break;
1053 default:
1054 Debug( LDAP_DEBUG_ACL, "aciValidatePerms: perms needs to be one of x,d,c,s,r,w in '%s'\n", perms->bv_val, 0, 0 );
1055 return LDAP_INVALID_SYNTAX;
1058 if ( ++i == perms->bv_len ) {
1059 return LDAP_SUCCESS;
1062 while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
1063 i++;
1065 assert( i != perms->bv_len );
1067 if ( perms->bv_val[ i ] != ',' ) {
1068 Debug( LDAP_DEBUG_ACL, "aciValidatePerms: missing comma in '%s'\n", perms->bv_val, 0, 0 );
1069 return LDAP_INVALID_SYNTAX;
1072 do {
1073 i++;
1074 } while ( perms->bv_val[ i ] == ' ' );
1077 return LDAP_SUCCESS;
1080 static const struct berval *ACIgrantdeny[] = {
1081 &aci_bv[ ACI_BV_GRANT ],
1082 &aci_bv[ ACI_BV_DENY ],
1083 NULL
1086 static int
1087 OpenLDAPaciValidateRight(
1088 struct berval *action )
1090 struct berval bv = BER_BVNULL;
1091 int i;
1093 /* grant|deny */
1094 if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
1095 bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
1097 Debug( LDAP_DEBUG_ACL, "aciValidateRight: '%s' must be either 'grant' or 'deny'\n", bv.bv_val, 0, 0 );
1098 return LDAP_INVALID_SYNTAX;
1101 for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
1102 if ( i & 1 ) {
1103 /* perms */
1104 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
1106 return LDAP_INVALID_SYNTAX;
1109 } else {
1110 /* attr */
1111 AttributeDescription *ad;
1112 const char *text;
1113 struct berval attr, left, right;
1114 int j;
1116 /* could be "[all]" or an attribute description */
1117 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
1118 continue;
1122 for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ )
1124 ad = NULL;
1125 text = NULL;
1126 if ( acl_get_part( &attr, 0, '=', &left ) < 0
1127 || acl_get_part( &attr, 1, '=', &right ) < 0 )
1129 if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS )
1131 Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", attr.bv_val, 0, 0 );
1132 return LDAP_INVALID_SYNTAX;
1134 } else {
1135 if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS )
1137 Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", left.bv_val, 0, 0 );
1138 return LDAP_INVALID_SYNTAX;
1145 /* "perms;attr" go in pairs */
1146 if ( i > 0 && ( i & 1 ) == 0 ) {
1147 return LDAP_SUCCESS;
1149 } else {
1150 Debug( LDAP_DEBUG_ACL, "aciValidateRight: perms:attr need to be pairs in '%s'\n", action->bv_val, 0, 0 );
1151 return LDAP_INVALID_SYNTAX;
1154 return LDAP_SUCCESS;
1157 static int
1158 OpenLDAPaciNormalizeRight(
1159 struct berval *action,
1160 struct berval *naction,
1161 void *ctx )
1163 struct berval grantdeny,
1164 perms = BER_BVNULL,
1165 bv = BER_BVNULL;
1166 int idx,
1169 /* grant|deny */
1170 if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
1171 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: missing ';' in '%s'\n", action->bv_val, 0, 0 );
1172 return LDAP_INVALID_SYNTAX;
1174 idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
1175 if ( idx == -1 ) {
1176 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: '%s' must be grant or deny\n", grantdeny.bv_val, 0, 0 );
1177 return LDAP_INVALID_SYNTAX;
1180 ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
1182 for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
1183 struct berval nattrs = BER_BVNULL;
1184 int freenattrs = 1;
1185 if ( i & 1 ) {
1186 /* perms */
1187 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
1189 return LDAP_INVALID_SYNTAX;
1191 perms = bv;
1193 } else {
1194 /* attr */
1195 char *ptr;
1197 /* could be "[all]" or an attribute description */
1198 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
1199 nattrs = aci_bv[ ACI_BV_BR_ALL ];
1200 freenattrs = 0;
1202 } else {
1203 AttributeDescription *ad = NULL;
1204 AttributeDescription adstatic= { 0 };
1205 const char *text = NULL;
1206 struct berval attr, left, right;
1207 int j;
1208 int len;
1210 for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ )
1212 ad = NULL;
1213 text = NULL;
1214 /* openldap 2.1 aci compabitibility [entry] -> entry */
1215 if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ENTRY ] ) == 0 ) {
1216 ad = &adstatic;
1217 adstatic.ad_cname = aci_bv[ ACI_BV_ENTRY ];
1219 /* openldap 2.1 aci compabitibility [children] -> children */
1220 } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_CHILDREN ] ) == 0 ) {
1221 ad = &adstatic;
1222 adstatic.ad_cname = aci_bv[ ACI_BV_CHILDREN ];
1224 /* openldap 2.1 aci compabitibility [all] -> only [all] */
1225 } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
1226 ber_memfree_x( nattrs.bv_val, ctx );
1227 nattrs = aci_bv[ ACI_BV_BR_ALL ];
1228 freenattrs = 0;
1229 break;
1231 } else if ( acl_get_part( &attr, 0, '=', &left ) < 0
1232 || acl_get_part( &attr, 1, '=', &right ) < 0 )
1234 if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS )
1236 ber_memfree_x( nattrs.bv_val, ctx );
1237 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", attr.bv_val, 0, 0 );
1238 return LDAP_INVALID_SYNTAX;
1241 } else {
1242 if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS )
1244 ber_memfree_x( nattrs.bv_val, ctx );
1245 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", left.bv_val, 0, 0 );
1246 return LDAP_INVALID_SYNTAX;
1251 len = nattrs.bv_len + ( !BER_BVISEMPTY( &nattrs ) ? STRLENOF( "," ) : 0 )
1252 + ad->ad_cname.bv_len;
1253 nattrs.bv_val = ber_memrealloc_x( nattrs.bv_val, len + 1, ctx );
1254 ptr = &nattrs.bv_val[ nattrs.bv_len ];
1255 if ( !BER_BVISEMPTY( &nattrs ) ) {
1256 *ptr++ = ',';
1258 ptr = lutil_strncopy( ptr, ad->ad_cname.bv_val, ad->ad_cname.bv_len );
1259 ptr[ 0 ] = '\0';
1260 nattrs.bv_len = len;
1265 naction->bv_val = ber_memrealloc_x( naction->bv_val,
1266 naction->bv_len + STRLENOF( ";" )
1267 + perms.bv_len + STRLENOF( ";" )
1268 + nattrs.bv_len + 1,
1269 ctx );
1271 ptr = &naction->bv_val[ naction->bv_len ];
1272 ptr[ 0 ] = ';';
1273 ptr++;
1274 ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
1275 ptr[ 0 ] = ';';
1276 ptr++;
1277 ptr = lutil_strncopy( ptr, nattrs.bv_val, nattrs.bv_len );
1278 ptr[ 0 ] = '\0';
1279 naction->bv_len += STRLENOF( ";" ) + perms.bv_len
1280 + STRLENOF( ";" ) + nattrs.bv_len;
1281 if ( freenattrs ) {
1282 ber_memfree_x( nattrs.bv_val, ctx );
1287 /* perms;attr go in pairs */
1288 if ( i > 1 && ( i & 1 ) ) {
1289 return LDAP_SUCCESS;
1291 } else {
1292 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: perms:attr need to be pairs in '%s'\n", action->bv_val, 0, 0 );
1293 return LDAP_INVALID_SYNTAX;
1297 static int
1298 OpenLDAPaciValidateRights(
1299 struct berval *actions )
1302 struct berval bv = BER_BVNULL;
1303 int i;
1305 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1306 if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
1307 return LDAP_INVALID_SYNTAX;
1311 return LDAP_SUCCESS;
1314 static int
1315 OpenLDAPaciNormalizeRights(
1316 struct berval *actions,
1317 struct berval *nactions,
1318 void *ctx )
1321 struct berval bv = BER_BVNULL;
1322 int i;
1324 BER_BVZERO( nactions );
1325 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1326 int rc;
1327 struct berval nbv;
1329 rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
1330 if ( rc != LDAP_SUCCESS ) {
1331 ber_memfree_x( nactions->bv_val, ctx );
1332 BER_BVZERO( nactions );
1333 return LDAP_INVALID_SYNTAX;
1336 if ( i == 0 ) {
1337 *nactions = nbv;
1339 } else {
1340 nactions->bv_val = ber_memrealloc_x( nactions->bv_val,
1341 nactions->bv_len + STRLENOF( "$" )
1342 + nbv.bv_len + 1,
1343 ctx );
1344 nactions->bv_val[ nactions->bv_len ] = '$';
1345 AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
1346 nbv.bv_val, nbv.bv_len + 1 );
1347 ber_memfree_x( nbv.bv_val, ctx );
1348 nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
1350 BER_BVZERO( &nbv );
1353 return LDAP_SUCCESS;
1356 static const struct berval *OpenLDAPaciscopes[] = {
1357 &aci_bv[ ACI_BV_ENTRY ],
1358 &aci_bv[ ACI_BV_CHILDREN ],
1359 &aci_bv[ ACI_BV_SUBTREE ],
1361 NULL
1364 static const struct berval *OpenLDAPacitypes[] = {
1365 /* DN-valued */
1366 &aci_bv[ ACI_BV_GROUP ],
1367 &aci_bv[ ACI_BV_ROLE ],
1369 /* set to one past the last DN-valued type with options (/) */
1370 #define LAST_OPTIONAL 2
1372 &aci_bv[ ACI_BV_ACCESS_ID ],
1373 &aci_bv[ ACI_BV_SUBTREE ],
1374 &aci_bv[ ACI_BV_ONELEVEL ],
1375 &aci_bv[ ACI_BV_CHILDREN ],
1377 /* set to one past the last DN-valued type */
1378 #define LAST_DNVALUED 6
1380 /* non DN-valued */
1381 &aci_bv[ ACI_BV_DNATTR ],
1382 &aci_bv[ ACI_BV_PUBLIC ],
1383 &aci_bv[ ACI_BV_USERS ],
1384 &aci_bv[ ACI_BV_SELF ],
1385 &aci_bv[ ACI_BV_SET ],
1386 &aci_bv[ ACI_BV_SET_REF ],
1388 NULL
1391 static int
1392 OpenLDAPaciValidate(
1393 Syntax *syntax,
1394 struct berval *val )
1396 struct berval oid = BER_BVNULL,
1397 scope = BER_BVNULL,
1398 rights = BER_BVNULL,
1399 type = BER_BVNULL,
1400 subject = BER_BVNULL;
1401 int idx;
1402 int rc;
1404 if ( BER_BVISEMPTY( val ) ) {
1405 Debug( LDAP_DEBUG_ACL, "aciValidatet: value is empty\n", 0, 0, 0 );
1406 return LDAP_INVALID_SYNTAX;
1409 /* oid */
1410 if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
1411 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1413 /* NOTE: the numericoidValidate() is rather pedantic;
1414 * I'd replace it with X-ORDERED VALUES so that
1415 * it's guaranteed values are maintained and used
1416 * in the desired order */
1417 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid oid '%s'\n", oid.bv_val, 0, 0 );
1418 return LDAP_INVALID_SYNTAX;
1421 /* scope */
1422 if ( acl_get_part( val, 1, '#', &scope ) < 0 ||
1423 bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
1425 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid scope '%s'\n", scope.bv_val, 0, 0 );
1426 return LDAP_INVALID_SYNTAX;
1429 /* rights */
1430 if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
1431 OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS )
1433 return LDAP_INVALID_SYNTAX;
1436 /* type */
1437 if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1438 Debug( LDAP_DEBUG_ACL, "aciValidate: missing type in '%s'\n", val->bv_val, 0, 0 );
1439 return LDAP_INVALID_SYNTAX;
1441 idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1442 if ( idx == -1 ) {
1443 struct berval isgr;
1445 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1446 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", type.bv_val, 0, 0 );
1447 return LDAP_INVALID_SYNTAX;
1450 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1451 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1452 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", isgr.bv_val, 0, 0 );
1453 return LDAP_INVALID_SYNTAX;
1457 /* subject */
1458 bv_get_tail( val, &type, &subject );
1459 if ( subject.bv_val[ 0 ] != '#' ) {
1460 Debug( LDAP_DEBUG_ACL, "aciValidate: missing subject in '%s'\n", val->bv_val, 0, 0 );
1461 return LDAP_INVALID_SYNTAX;
1464 if ( idx >= LAST_DNVALUED ) {
1465 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1466 AttributeDescription *ad = NULL;
1467 const char *text = NULL;
1469 rc = slap_bv2ad( &subject, &ad, &text );
1470 if ( rc != LDAP_SUCCESS ) {
1471 Debug( LDAP_DEBUG_ACL, "aciValidate: unknown dn attribute '%s'\n", subject.bv_val, 0, 0 );
1472 return LDAP_INVALID_SYNTAX;
1475 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1476 /* FIXME: allow nameAndOptionalUID? */
1477 Debug( LDAP_DEBUG_ACL, "aciValidate: wrong syntax for dn attribute '%s'\n", subject.bv_val, 0, 0 );
1478 return LDAP_INVALID_SYNTAX;
1482 /* not a DN */
1483 return LDAP_SUCCESS;
1485 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1486 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1488 /* do {group|role}/oc/at check */
1489 struct berval ocbv = BER_BVNULL,
1490 atbv = BER_BVNULL;
1492 ocbv.bv_val = ber_bvchr( &type, '/' );
1493 if ( ocbv.bv_val != NULL ) {
1494 ocbv.bv_val++;
1495 ocbv.bv_len = type.bv_len
1496 - ( ocbv.bv_val - type.bv_val );
1498 atbv.bv_val = ber_bvchr( &ocbv, '/' );
1499 if ( atbv.bv_val != NULL ) {
1500 AttributeDescription *ad = NULL;
1501 const char *text = NULL;
1502 int rc;
1504 atbv.bv_val++;
1505 atbv.bv_len = type.bv_len
1506 - ( atbv.bv_val - type.bv_val );
1507 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1509 rc = slap_bv2ad( &atbv, &ad, &text );
1510 if ( rc != LDAP_SUCCESS ) {
1511 Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group attribute '%s'\n", atbv.bv_val, 0, 0 );
1512 return LDAP_INVALID_SYNTAX;
1516 if ( oc_bvfind( &ocbv ) == NULL ) {
1517 Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group '%s'\n", ocbv.bv_val, 0, 0 );
1518 return LDAP_INVALID_SYNTAX;
1523 if ( BER_BVISEMPTY( &subject ) ) {
1524 /* empty DN invalid */
1525 Debug( LDAP_DEBUG_ACL, "aciValidate: missing dn in '%s'\n", val->bv_val, 0, 0 );
1526 return LDAP_INVALID_SYNTAX;
1529 subject.bv_val++;
1530 subject.bv_len--;
1532 /* FIXME: pass DN syntax? */
1533 rc = dnValidate( NULL, &subject );
1534 if ( rc != LDAP_SUCCESS ) {
1535 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid dn '%s'\n", subject.bv_val, 0, 0 );
1537 return rc;
1540 static int
1541 OpenLDAPaciPrettyNormal(
1542 struct berval *val,
1543 struct berval *out,
1544 void *ctx,
1545 int normalize )
1547 struct berval oid = BER_BVNULL,
1548 scope = BER_BVNULL,
1549 rights = BER_BVNULL,
1550 nrights = BER_BVNULL,
1551 type = BER_BVNULL,
1552 ntype = BER_BVNULL,
1553 subject = BER_BVNULL,
1554 nsubject = BER_BVNULL;
1555 int idx,
1556 rc = LDAP_SUCCESS,
1557 freesubject = 0,
1558 freetype = 0;
1559 char *ptr;
1561 BER_BVZERO( out );
1563 if ( BER_BVISEMPTY( val ) ) {
1564 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: value is empty\n", 0, 0, 0 );
1565 return LDAP_INVALID_SYNTAX;
1568 /* oid: if valid, it's already normalized */
1569 if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
1570 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1572 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid oid '%s'\n", oid.bv_val, 0, 0 );
1573 return LDAP_INVALID_SYNTAX;
1576 /* scope: normalize by replacing with OpenLDAPaciscopes */
1577 if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
1578 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing scope in '%s'\n", val->bv_val, 0, 0 );
1579 return LDAP_INVALID_SYNTAX;
1581 idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
1582 if ( idx == -1 ) {
1583 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid scope '%s'\n", scope.bv_val, 0, 0 );
1584 return LDAP_INVALID_SYNTAX;
1586 scope = *OpenLDAPaciscopes[ idx ];
1588 /* rights */
1589 if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
1590 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing rights in '%s'\n", val->bv_val, 0, 0 );
1591 return LDAP_INVALID_SYNTAX;
1593 if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
1594 != LDAP_SUCCESS )
1596 return LDAP_INVALID_SYNTAX;
1599 /* type */
1600 if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1601 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing type in '%s'\n", val->bv_val, 0, 0 );
1602 rc = LDAP_INVALID_SYNTAX;
1603 goto cleanup;
1605 idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1606 if ( idx == -1 ) {
1607 struct berval isgr;
1609 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1610 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", type.bv_val, 0, 0 );
1611 rc = LDAP_INVALID_SYNTAX;
1612 goto cleanup;
1615 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1616 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1617 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", isgr.bv_val, 0, 0 );
1618 rc = LDAP_INVALID_SYNTAX;
1619 goto cleanup;
1622 ntype = *OpenLDAPacitypes[ idx ];
1624 /* subject */
1625 bv_get_tail( val, &type, &subject );
1627 if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
1628 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing subject in '%s'\n", val->bv_val, 0, 0 );
1629 rc = LDAP_INVALID_SYNTAX;
1630 goto cleanup;
1633 subject.bv_val++;
1634 subject.bv_len--;
1636 if ( idx < LAST_DNVALUED ) {
1637 /* FIXME: pass DN syntax? */
1638 if ( normalize ) {
1639 rc = dnNormalize( 0, NULL, NULL,
1640 &subject, &nsubject, ctx );
1641 } else {
1642 rc = dnPretty( NULL, &subject, &nsubject, ctx );
1645 if ( rc == LDAP_SUCCESS ) {
1646 freesubject = 1;
1648 } else {
1649 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid subject dn '%s'\n", subject.bv_val, 0, 0 );
1650 goto cleanup;
1653 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1654 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1656 /* do {group|role}/oc/at check */
1657 struct berval ocbv = BER_BVNULL,
1658 atbv = BER_BVNULL;
1660 ocbv.bv_val = ber_bvchr( &type, '/' );
1661 if ( ocbv.bv_val != NULL ) {
1662 ObjectClass *oc = NULL;
1663 AttributeDescription *ad = NULL;
1664 const char *text = NULL;
1665 int rc;
1666 struct berval bv;
1668 bv.bv_len = ntype.bv_len;
1670 ocbv.bv_val++;
1671 ocbv.bv_len = type.bv_len - ( ocbv.bv_val - type.bv_val );
1673 atbv.bv_val = ber_bvchr( &ocbv, '/' );
1674 if ( atbv.bv_val != NULL ) {
1675 atbv.bv_val++;
1676 atbv.bv_len = type.bv_len
1677 - ( atbv.bv_val - type.bv_val );
1678 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1680 rc = slap_bv2ad( &atbv, &ad, &text );
1681 if ( rc != LDAP_SUCCESS ) {
1682 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown group attribute '%s'\n", atbv.bv_val, 0, 0 );
1683 rc = LDAP_INVALID_SYNTAX;
1684 goto cleanup;
1687 bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
1690 oc = oc_bvfind( &ocbv );
1691 if ( oc == NULL ) {
1692 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid group '%s'\n", ocbv.bv_val, 0, 0 );
1693 rc = LDAP_INVALID_SYNTAX;
1694 goto cleanup;
1697 bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
1698 bv.bv_val = ber_memalloc_x( bv.bv_len + 1, ctx );
1700 ptr = bv.bv_val;
1701 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1702 ptr[ 0 ] = '/';
1703 ptr++;
1704 ptr = lutil_strncopy( ptr,
1705 oc->soc_cname.bv_val,
1706 oc->soc_cname.bv_len );
1707 if ( ad != NULL ) {
1708 ptr[ 0 ] = '/';
1709 ptr++;
1710 ptr = lutil_strncopy( ptr,
1711 ad->ad_cname.bv_val,
1712 ad->ad_cname.bv_len );
1714 ptr[ 0 ] = '\0';
1716 ntype = bv;
1717 freetype = 1;
1721 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1722 AttributeDescription *ad = NULL;
1723 const char *text = NULL;
1724 int rc;
1726 rc = slap_bv2ad( &subject, &ad, &text );
1727 if ( rc != LDAP_SUCCESS ) {
1728 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown dn attribute '%s'\n", subject.bv_val, 0, 0 );
1729 rc = LDAP_INVALID_SYNTAX;
1730 goto cleanup;
1733 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1734 /* FIXME: allow nameAndOptionalUID? */
1735 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: wrong syntax for dn attribute '%s'\n", subject.bv_val, 0, 0 );
1736 rc = LDAP_INVALID_SYNTAX;
1737 goto cleanup;
1740 nsubject = ad->ad_cname;
1744 out->bv_len =
1745 oid.bv_len + STRLENOF( "#" )
1746 + scope.bv_len + STRLENOF( "#" )
1747 + nrights.bv_len + STRLENOF( "#" )
1748 + ntype.bv_len + STRLENOF( "#" )
1749 + nsubject.bv_len;
1751 out->bv_val = ber_memalloc_x( out->bv_len + 1, ctx );
1752 ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
1753 ptr[ 0 ] = '#';
1754 ptr++;
1755 ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
1756 ptr[ 0 ] = '#';
1757 ptr++;
1758 ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
1759 ptr[ 0 ] = '#';
1760 ptr++;
1761 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1762 ptr[ 0 ] = '#';
1763 ptr++;
1764 if ( !BER_BVISNULL( &nsubject ) ) {
1765 ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
1767 ptr[ 0 ] = '\0';
1769 cleanup:;
1770 if ( freesubject ) {
1771 ber_memfree_x( nsubject.bv_val, ctx );
1774 if ( freetype ) {
1775 ber_memfree_x( ntype.bv_val, ctx );
1778 if ( !BER_BVISNULL( &nrights ) ) {
1779 ber_memfree_x( nrights.bv_val, ctx );
1782 return rc;
1785 static int
1786 OpenLDAPaciPretty(
1787 Syntax *syntax,
1788 struct berval *val,
1789 struct berval *out,
1790 void *ctx )
1792 return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
1795 static int
1796 OpenLDAPaciNormalize(
1797 slap_mask_t use,
1798 Syntax *syntax,
1799 MatchingRule *mr,
1800 struct berval *val,
1801 struct berval *out,
1802 void *ctx )
1804 return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
1807 #if SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC
1809 * FIXME: need config and Makefile.am code to ease building
1810 * as dynamic module
1813 init_module( int argc, char *argv[] )
1815 return dynacl_aci_init();
1817 #endif /* SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC */
1819 #endif /* SLAPD_ACI_ENABLED */