1 /* aci.c - routines to parse and check acl's */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 1998-2021 The OpenLDAP Foundation.
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 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
17 * All rights reserved.
18 *
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.
25 */
26
27 #include "portable.h"
28
29 #ifdef SLAPD_ACI_ENABLED
30
31 #include <stdio.h>
32
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>
38
39 #include "slap.h"
40 #include "lber_pvt.h"
41 #include "lutil.h"
42
43 /* use most appropriate size */
44 #define ACI_BUF_SIZE 1024
45
46 /* move to "stable" when no longer experimental */
47 #define SLAPD_ACI_SYNTAX "1.3.6.1.4.1.4203.666.2.1"
48
49 /* change this to "OpenLDAPset" */
50 #define SLAPD_ACI_SET_ATTR "template"
51
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;
57
58 enum {
59 ACI_BV_ENTRY,
60 ACI_BV_CHILDREN,
61 ACI_BV_ONELEVEL,
62 ACI_BV_SUBTREE,
63
64 ACI_BV_BR_ENTRY,
65 ACI_BV_BR_CHILDREN,
66 ACI_BV_BR_ALL,
67
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,
77
78 ACI_BV_GRANT,
79 ACI_BV_DENY,
80
81 ACI_BV_GROUP_CLASS,
82 ACI_BV_GROUP_ATTR,
83 ACI_BV_ROLE_CLASS,
84 ACI_BV_ROLE_ATTR,
85
86 ACI_BV_SET_ATTR,
87
88 ACI_BV_LAST
89 };
90
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"),
97
98 /* */
99 BER_BVC("[entry]"),
100 BER_BVC("[children]"),
101 BER_BVC("[all]"),
102
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"),
113
114 /* actions */
115 BER_BVC("grant"),
116 BER_BVC("deny"),
117
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),
123
124 BER_BVC(SLAPD_ACI_SET_ATTR),
125
126 BER_BVNULL
127 };
128
129 static AttributeDescription *slap_ad_aci;
130
131 static int
132 OpenLDAPaciValidate(
133 Syntax *syntax,
134 struct berval *val );
135
136 static int
137 OpenLDAPaciPretty(
138 Syntax *syntax,
139 struct berval *val,
140 struct berval *out,
141 void *ctx );
142
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 );
151
152 #define OpenLDAPaciMatch octetStringMatch
153
154 static int
aci_list_map_rights(struct berval * list)155 aci_list_map_rights(
156 struct berval *list )
157 {
158 struct berval bv;
159 slap_access_t mask;
160 int i;
161
162 ACL_INIT( mask );
163 for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
164 if ( bv.bv_len <= 0 ) {
165 continue;
166 }
167
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.
173 */
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.
180 */
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.
191 */
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;
202 }
203
204 }
205
206 return mask;
207 }
208
209 static int
aci_list_has_attr(struct berval * list,const struct berval * attr,struct berval * val)210 aci_list_has_attr(
211 struct berval *list,
212 const struct berval *attr,
213 struct berval *val )
214 {
215 struct berval bv, left, right;
216 int i;
217
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 )
221 {
222 if ( ber_bvstrcasecmp( attr, &bv ) == 0 ) {
223 return(1);
224 }
225
226 } else if ( val == NULL ) {
227 if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
228 return(1);
229 }
230
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.
245 *
246 * Ideally, this would allow r.e. matches.
247 */
248 if ( acl_get_part( &right, 0, '*', &left ) < 0
249 || right.bv_len <= left.bv_len )
250 {
251 if ( ber_bvstrcasecmp( val, &right ) == 0 ) {
252 return 1;
253 }
254
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);
258 }
259 }
260 }
261 }
262 }
263
264 return 0;
265 }
266
267 static slap_access_t
aci_list_get_attr_rights(struct berval * list,const struct berval * attr,struct berval * val)268 aci_list_get_attr_rights(
269 struct berval *list,
270 const struct berval *attr,
271 struct berval *val )
272 {
273 struct berval bv;
274 slap_access_t mask;
275 int i;
276
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 );
285 continue;
286 }
287
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 );
292
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 continue;
298 }
299
300 mask |= aci_list_map_rights( &bv );
301 Debug( LDAP_DEBUG_ACL,
302 " <= aci_list_get_attr_rights "
303 "rights %s to mask 0x%x\n",
304 bv.bv_val, mask );
305 }
306
307 return mask;
308 }
309
310 static int
aci_list_get_rights(struct berval * list,struct berval * attr,struct berval * val,slap_access_t * grant,slap_access_t * deny)311 aci_list_get_rights(
312 struct berval *list,
313 struct berval *attr,
314 struct berval *val,
315 slap_access_t *grant,
316 slap_access_t *deny )
317 {
318 struct berval perm, actn, baseattr;
319 slap_access_t *mask;
320 int i, found;
321
322 if ( attr == NULL || BER_BVISEMPTY( attr ) ) {
323 attr = (struct berval *)&aci_bv[ ACI_BV_ENTRY ];
324
325 } else if ( acl_get_part( attr, 0, ';', &baseattr ) > 0 ) {
326 attr = &baseattr;
327 }
328 found = 0;
329 ACL_INIT(*grant);
330 ACL_INIT(*deny);
331 /* loop through each permissions clause */
332 for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) {
333 if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) {
334 continue;
335 }
336
337 if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) {
338 mask = grant;
339
340 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DENY ], &actn ) == 0 ) {
341 mask = deny;
342
343 } else {
344 continue;
345 }
346
347 *mask |= aci_list_get_attr_rights( &perm, attr, val );
348 *mask |= aci_list_get_attr_rights( &perm, &aci_bv[ ACI_BV_BR_ALL ], NULL );
349
350 if ( *mask != ACL_PRIV_NONE ) {
351 found = 1;
352 }
353 }
354
355 return found;
356 }
357
358 static int
aci_group_member(struct berval * subj,const struct berval * defgrpoc,const struct berval * defgrpat,Operation * op,Entry * e,int nmatch,regmatch_t * matches)359 aci_group_member (
360 struct berval *subj,
361 const struct berval *defgrpoc,
362 const struct berval *defgrpat,
363 Operation *op,
364 Entry *e,
365 int nmatch,
366 regmatch_t *matches
367 )
368 {
369 struct berval subjdn;
370 struct berval grpoc;
371 struct berval grpat;
372 ObjectClass *grp_oc = NULL;
373 AttributeDescription *grp_ad = NULL;
374 const char *text;
375 int rc;
376
377 /* format of string is "{group|role}/objectClassValue/groupAttrName" */
378 if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
379 return 0;
380 }
381
382 if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) {
383 grpoc = *defgrpoc;
384 }
385
386 if ( acl_get_part( subj, 2, '/', &grpat ) < 0 ) {
387 grpat = *defgrpat;
388 }
389
390 rc = slap_bv2ad( &grpat, &grp_ad, &text );
391 if ( rc != LDAP_SUCCESS ) {
392 rc = 0;
393 goto done;
394 }
395 rc = 0;
396
397 grp_oc = oc_bvfind( &grpoc );
398
399 if ( grp_oc != NULL && grp_ad != NULL ) {
400 char buf[ ACI_BUF_SIZE ];
401 struct berval bv, ndn;
402 AclRegexMatches amatches = { 0 };
403
404 amatches.dn_count = nmatch;
405 AC_MEMCPY( amatches.dn_data, matches, sizeof( amatches.dn_data ) );
406
407 bv.bv_len = sizeof( buf ) - 1;
408 bv.bv_val = (char *)&buf;
409 if ( acl_string_expand( &bv, &subjdn,
410 &e->e_nname, NULL, &amatches ) )
411 {
412 rc = LDAP_OTHER;
413 goto done;
414 }
415
416 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
417 {
418 rc = ( backend_group( op, e, &ndn, &op->o_ndn,
419 grp_oc, grp_ad ) == 0 );
420 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
421 }
422 }
423
424 done:
425 return rc;
426 }
427
428 static int
aci_mask(Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,struct berval * aci,int nmatch,regmatch_t * matches,slap_access_t * grant,slap_access_t * deny,slap_aci_scope_t asserted_scope)429 aci_mask(
430 Operation *op,
431 Entry *e,
432 AttributeDescription *desc,
433 struct berval *val,
434 struct berval *aci,
435 int nmatch,
436 regmatch_t *matches,
437 slap_access_t *grant,
438 slap_access_t *deny,
439 slap_aci_scope_t asserted_scope )
440 {
441 struct berval bv,
442 scope,
443 perms,
444 type,
445 opts,
446 sdn;
447 int rc;
448
449 ACL_INIT( *grant );
450 ACL_INIT( *deny );
451
452 assert( !BER_BVISNULL( &desc->ad_cname ) );
453
454 /* parse an aci of the form:
455 oid # scope # action;rights;attr;rights;attr
456 $ action;rights;attr;rights;attr # type # subject
457
458 [NOTE: the following comment is very outdated,
459 as the draft version it refers to (Ando, 2004-11-20)].
460
461 See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
462 a full description of the format for this attribute.
463 Differences: "this" in the draft is "self" here, and
464 "self" and "public" is in the position of type.
465
466 <scope> = {entry|children|subtree}
467 <type> = {public|users|access-id|subtree|onelevel|children|
468 self|dnattr|group|role|set|set-ref}
469
470 This routine now supports scope={ENTRY,CHILDREN}
471 with the semantics:
472 - ENTRY applies to "entry" and "subtree";
473 - CHILDREN applies to "children" and "subtree"
474 */
475
476 /* check that the aci has all 5 components */
477 if ( acl_get_part( aci, 4, '#', NULL ) < 0 ) {
478 return 0;
479 }
480
481 /* check that the aci family is supported */
482 /* FIXME: the OID is ignored? */
483 if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) {
484 return 0;
485 }
486
487 /* check that the scope matches */
488 if ( acl_get_part( aci, 1, '#', &scope ) < 0 ) {
489 return 0;
490 }
491
492 /* note: scope can be either ENTRY or CHILDREN;
493 * they respectively match "entry" and "children" in bv
494 * both match "subtree" */
495 switch ( asserted_scope ) {
496 case SLAP_ACI_SCOPE_ENTRY:
497 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
498 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
499 {
500 return 0;
501 }
502 break;
503
504 case SLAP_ACI_SCOPE_CHILDREN:
505 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
506 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
507 {
508 return 0;
509 }
510 break;
511
512 case SLAP_ACI_SCOPE_SUBTREE:
513 /* TODO: add assertion? */
514 return 0;
515 }
516
517 /* get the list of permissions clauses, bail if empty */
518 if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) {
519 assert( 0 );
520 return 0;
521 }
522
523 /* check if any permissions allow desired access */
524 if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
525 return 0;
526 }
527
528 /* see if we have a DN match */
529 if ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
530 assert( 0 );
531 return 0;
532 }
533
534 /* see if we have a public (i.e. anonymous) access */
535 if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
536 return 1;
537 }
538
539 /* otherwise require an identity */
540 if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
541 return 0;
542 }
543
544 /* see if we have a users access */
545 if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
546 return 1;
547 }
548
549 /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
550 * just grab all the berval up to its end (ITS#3303).
551 * NOTE: the problem could be solved by providing the DN with
552 * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would
553 * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
554 #if 0
555 if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) {
556 return 0;
557 }
558 #endif
559 sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
560 sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
561
562 /* get the type options, if any */
563 if ( acl_get_part( &type, 1, '/', &opts ) > 0 ) {
564 opts.bv_len = type.bv_len - ( opts.bv_val - type.bv_val );
565 type.bv_len = opts.bv_val - type.bv_val - 1;
566
567 } else {
568 BER_BVZERO( &opts );
569 }
570
571 if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
572 return dn_match( &op->o_ndn, &sdn );
573
574 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
575 return dnIsSuffix( &op->o_ndn, &sdn );
576
577 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
578 struct berval pdn;
579
580 dnParent( &sdn, &pdn );
581
582 return dn_match( &op->o_ndn, &pdn );
583
584 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
585 return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) );
586
587 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
588 return dn_match( &op->o_ndn, &e->e_nname );
589
590 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
591 Attribute *at;
592 AttributeDescription *ad = NULL;
593 const char *text;
594
595 rc = slap_bv2ad( &sdn, &ad, &text );
596 assert( rc == LDAP_SUCCESS );
597
598 rc = 0;
599 for ( at = attrs_find( e->e_attrs, ad );
600 at != NULL;
601 at = attrs_find( at->a_next, ad ) )
602 {
603 if ( attr_valfind( at,
604 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
605 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
606 &op->o_ndn, NULL, op->o_tmpmemctx ) == 0 )
607 {
608 rc = 1;
609 break;
610 }
611 }
612
613 return rc;
614
615 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
616 struct berval oc,
617 at;
618
619 if ( BER_BVISNULL( &opts ) ) {
620 oc = aci_bv[ ACI_BV_GROUP_CLASS ];
621 at = aci_bv[ ACI_BV_GROUP_ATTR ];
622
623 } else {
624 if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
625 assert( 0 );
626 }
627
628 if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
629 at = aci_bv[ ACI_BV_GROUP_ATTR ];
630 }
631 }
632
633 if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
634 {
635 return 1;
636 }
637
638 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
639 struct berval oc,
640 at;
641
642 if ( BER_BVISNULL( &opts ) ) {
643 oc = aci_bv[ ACI_BV_ROLE_CLASS ];
644 at = aci_bv[ ACI_BV_ROLE_ATTR ];
645
646 } else {
647 if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
648 assert( 0 );
649 }
650
651 if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
652 at = aci_bv[ ACI_BV_ROLE_ATTR ];
653 }
654 }
655
656 if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
657 {
658 return 1;
659 }
660
661 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
662 if ( acl_match_set( &sdn, op, e, NULL ) ) {
663 return 1;
664 }
665
666 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
667 if ( acl_match_set( &sdn, op, e, (struct berval *)&aci_bv[ ACI_BV_SET_ATTR ] ) ) {
668 return 1;
669 }
670
671 } else {
672 /* it passed normalization! */
673 assert( 0 );
674 }
675
676 return 0;
677 }
678
679 static int
aci_init(void)680 aci_init( void )
681 {
682 /* OpenLDAP eXperimental Syntax */
683 static slap_syntax_defs_rec aci_syntax_def = {
684 "( 1.3.6.1.4.1.4203.666.2.1 DESC 'OpenLDAP Experimental ACI' )",
685 SLAP_SYNTAX_HIDE,
686 NULL,
687 OpenLDAPaciValidate,
688 OpenLDAPaciPretty
689 };
690 static slap_mrule_defs_rec aci_mr_def = {
691 "( 1.3.6.1.4.1.4203.666.4.2 NAME 'OpenLDAPaciMatch' "
692 "SYNTAX 1.3.6.1.4.1.4203.666.2.1 )",
693 SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
694 NULL, OpenLDAPaciNormalize, OpenLDAPaciMatch,
695 NULL, NULL,
696 NULL
697 };
698 static struct {
699 char *name;
700 char *desc;
701 slap_mask_t flags;
702 AttributeDescription **ad;
703 } aci_at = {
704 "OpenLDAPaci", "( 1.3.6.1.4.1.4203.666.1.5 "
705 "NAME 'OpenLDAPaci' "
706 "DESC 'OpenLDAP access control information (experimental)' "
707 "EQUALITY OpenLDAPaciMatch "
708 "SYNTAX 1.3.6.1.4.1.4203.666.2.1 "
709 "USAGE directoryOperation )",
710 SLAP_AT_HIDE,
711 &slap_ad_aci
712 };
713
714 int rc;
715
716 /* ACI syntax */
717 rc = register_syntax( &aci_syntax_def );
718 if ( rc != 0 ) {
719 return rc;
720 }
721
722 /* ACI equality rule */
723 rc = register_matching_rule( &aci_mr_def );
724 if ( rc != 0 ) {
725 return rc;
726 }
727
728 /* ACI attribute */
729 rc = register_at( aci_at.desc, aci_at.ad, 0 );
730 if ( rc != LDAP_SUCCESS ) {
731 Debug( LDAP_DEBUG_ANY,
732 "aci_init: at_register failed\n" );
733 return rc;
734 }
735
736 /* install flags */
737 (*aci_at.ad)->ad_type->sat_flags |= aci_at.flags;
738
739 return rc;
740 }
741
742 static int
dynacl_aci_parse(const char * fname,int lineno,const char * opts,slap_style_t sty,const char * right,void ** privp)743 dynacl_aci_parse(
744 const char *fname,
745 int lineno,
746 const char *opts,
747 slap_style_t sty,
748 const char *right,
749 void **privp )
750 {
751 AttributeDescription *ad = NULL;
752 const char *text = NULL;
753
754 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
755 fprintf( stderr, "%s: line %d: "
756 "inappropriate style \"%s\" in \"aci\" by clause\n",
757 fname, lineno, style_strings[sty] );
758 return -1;
759 }
760
761 if ( right != NULL && *right != '\0' ) {
762 if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
763 fprintf( stderr,
764 "%s: line %d: aci \"%s\": %s\n",
765 fname, lineno, right, text );
766 return -1;
767 }
768
769 } else {
770 ad = slap_ad_aci;
771 }
772
773 if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
774 fprintf( stderr, "%s: line %d: "
775 "aci \"%s\": inappropriate syntax: %s\n",
776 fname, lineno, right,
777 ad->ad_type->sat_syntax_oid );
778 return -1;
779 }
780
781 *privp = (void *)ad;
782
783 return 0;
784 }
785
786 static int
dynacl_aci_unparse(void * priv,struct berval * bv)787 dynacl_aci_unparse( void *priv, struct berval *bv )
788 {
789 AttributeDescription *ad = ( AttributeDescription * )priv;
790 char *ptr;
791
792 assert( ad != NULL );
793
794 bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
795 ptr = lutil_strcopy( bv->bv_val, " aci=" );
796 ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
797 bv->bv_len = ptr - bv->bv_val;
798
799 return 0;
800 }
801
802 static int
dynacl_aci_mask(void * priv,Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,int nmatch,regmatch_t * matches,slap_access_t * grantp,slap_access_t * denyp)803 dynacl_aci_mask(
804 void *priv,
805 Operation *op,
806 Entry *e,
807 AttributeDescription *desc,
808 struct berval *val,
809 int nmatch,
810 regmatch_t *matches,
811 slap_access_t *grantp,
812 slap_access_t *denyp )
813 {
814 AttributeDescription *ad = ( AttributeDescription * )priv;
815 Attribute *at;
816 slap_access_t tgrant, tdeny, grant, deny;
817 #ifdef LDAP_DEBUG
818 char accessmaskbuf[ACCESSMASK_MAXLEN];
819 char accessmaskbuf1[ACCESSMASK_MAXLEN];
820 #endif /* LDAP_DEBUG */
821
822 if ( BER_BVISEMPTY( &e->e_nname ) ) {
823 /* no ACIs in the root DSE */
824 return -1;
825 }
826
827 /* start out with nothing granted, nothing denied */
828 ACL_INIT(tgrant);
829 ACL_INIT(tdeny);
830
831 /* get the aci attribute */
832 at = attr_find( e->e_attrs, ad );
833 if ( at != NULL ) {
834 int i;
835
836 /* the aci is an multi-valued attribute. The
837 * rights are determined by OR'ing the individual
838 * rights given by the acis.
839 */
840 for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
841 if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
842 nmatch, matches, &grant, &deny,
843 SLAP_ACI_SCOPE_ENTRY ) != 0 )
844 {
845 tgrant |= grant;
846 tdeny |= deny;
847 }
848 }
849
850 Debug( LDAP_DEBUG_ACL, " <= aci_mask grant %s deny %s\n",
851 accessmask2str( tgrant, accessmaskbuf, 1 ),
852 accessmask2str( tdeny, accessmaskbuf1, 1 ) );
853 }
854
855 /* If the entry level aci didn't contain anything valid for the
856 * current operation, climb up the tree and evaluate the
857 * acis with scope set to subtree
858 */
859 if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
860 struct berval parent_ndn;
861
862 dnParent( &e->e_nname, &parent_ndn );
863 while ( !BER_BVISEMPTY( &parent_ndn ) ){
864 int i;
865 BerVarray bvals = NULL;
866 int ret, stop;
867
868 /* to solve the chicken'n'egg problem of accessing
869 * the OpenLDAPaci attribute, the direct access
870 * to the entry's attribute is unchecked; however,
871 * further accesses to OpenLDAPaci values in the
872 * ancestors occur through backend_attribute(), i.e.
873 * with the identity of the operation, requiring
874 * further access checking. For uniformity, this
875 * makes further requests occur as the rootdn, if
876 * any, i.e. searching for the OpenLDAPaci attribute
877 * is considered an internal search. If this is not
878 * acceptable, then the same check needs be performed
879 * when accessing the entry's attribute. */
880 struct berval save_o_dn, save_o_ndn;
881
882 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
883 save_o_dn = op->o_dn;
884 save_o_ndn = op->o_ndn;
885
886 op->o_dn = op->o_bd->be_rootdn;
887 op->o_ndn = op->o_bd->be_rootndn;
888 }
889
890 Debug( LDAP_DEBUG_ACL, " checking ACI of \"%s\"\n", parent_ndn.bv_val );
891 ret = backend_attribute( op, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
892
893 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
894 op->o_dn = save_o_dn;
895 op->o_ndn = save_o_ndn;
896 }
897
898 switch ( ret ) {
899 case LDAP_SUCCESS :
900 stop = 0;
901 if ( !bvals ) {
902 break;
903 }
904
905 for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
906 if ( aci_mask( op, e, desc, val,
907 &bvals[i],
908 nmatch, matches,
909 &grant, &deny,
910 SLAP_ACI_SCOPE_CHILDREN ) != 0 )
911 {
912 tgrant |= grant;
913 tdeny |= deny;
914 /* evaluation stops as soon as either a "deny" or a
915 * "grant" directive matches.
916 */
917 if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
918 stop = 1;
919 }
920 }
921 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
922 accessmask2str( tgrant, accessmaskbuf, 1 ),
923 accessmask2str( tdeny, accessmaskbuf1, 1 ) );
924 }
925 break;
926
927 case LDAP_NO_SUCH_ATTRIBUTE:
928 /* just go on if the aci-Attribute is not present in
929 * the current entry
930 */
931 Debug( LDAP_DEBUG_ACL, "no such attribute\n" );
932 stop = 0;
933 break;
934
935 case LDAP_NO_SUCH_OBJECT:
936 /* We have reached the base object */
937 Debug( LDAP_DEBUG_ACL, "no such object\n" );
938 stop = 1;
939 break;
940
941 default:
942 stop = 1;
943 break;
944 }
945
946 if ( stop ) {
947 break;
948 }
949 dnParent( &parent_ndn, &parent_ndn );
950 }
951 }
952
953 *grantp = tgrant;
954 *denyp = tdeny;
955
956 return 0;
957 }
958
959 /* need to register this at some point */
960 static slap_dynacl_t dynacl_aci = {
961 "aci",
962 dynacl_aci_parse,
963 dynacl_aci_unparse,
964 dynacl_aci_mask,
965 NULL,
966 NULL,
967 NULL
968 };
969
970 int
dynacl_aci_init(void)971 dynacl_aci_init( void )
972 {
973 int rc;
974
975 rc = aci_init();
976
977 if ( rc == 0 ) {
978 rc = slap_dynacl_register( &dynacl_aci );
979 }
980
981 return rc;
982 }
983
984
985 /* ACI syntax validation */
986
987 /*
988 * Matches given berval to array of bervals
989 * Returns:
990 * >=0 if one if the array elements equals to this berval
991 * -1 if string was not found in array
992 */
993 static int
bv_getcaseidx(struct berval * bv,const struct berval * arr[])994 bv_getcaseidx(
995 struct berval *bv,
996 const struct berval *arr[] )
997 {
998 int i;
999
1000 if ( BER_BVISEMPTY( bv ) ) {
1001 return -1;
1002 }
1003
1004 for ( i = 0; arr[ i ] != NULL ; i++ ) {
1005 if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) {
1006 return i;
1007 }
1008 }
1009
1010 return -1;
1011 }
1012
1013
1014 /* Returns what have left in input berval after current sub */
1015 static void
bv_get_tail(struct berval * val,struct berval * sub,struct berval * tail)1016 bv_get_tail(
1017 struct berval *val,
1018 struct berval *sub,
1019 struct berval *tail )
1020 {
1021 int head_len;
1022
1023 tail->bv_val = sub->bv_val + sub->bv_len;
1024 head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val;
1025 tail->bv_len = val->bv_len - head_len;
1026 }
1027
1028
1029 /*
1030 * aci is accepted in following form:
1031 * oid#scope#rights#type#subject
1032 * Where:
1033 * oid := numeric OID (currently ignored)
1034 * scope := entry|children|subtree
1035 * rights := right[[$right]...]
1036 * right := (grant|deny);action
1037 * action := perms;attrs[[;perms;attrs]...]
1038 * perms := perm[[,perm]...]
1039 * perm := c|s|r|w|x
1040 * attrs := attribute[[,attribute]..]|"[all]"
1041 * attribute := attributeType|attributeType=attributeValue|attributeType=attributeValuePrefix*
1042 * type := public|users|self|dnattr|group|role|set|set-ref|
1043 * access_id|subtree|onelevel|children
1044 */
1045 static int
OpenLDAPaciValidatePerms(struct berval * perms)1046 OpenLDAPaciValidatePerms(
1047 struct berval *perms )
1048 {
1049 ber_len_t i;
1050
1051 for ( i = 0; i < perms->bv_len; ) {
1052 switch ( perms->bv_val[ i ] ) {
1053 case 'x':
1054 case 'd':
1055 case 'c':
1056 case 's':
1057 case 'r':
1058 case 'w':
1059 break;
1060
1061 default:
1062 Debug( LDAP_DEBUG_ACL, "aciValidatePerms: perms needs to be one of x,d,c,s,r,w in '%s'\n", perms->bv_val );
1063 return LDAP_INVALID_SYNTAX;
1064 }
1065
1066 if ( ++i == perms->bv_len ) {
1067 return LDAP_SUCCESS;
1068 }
1069
1070 while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
1071 i++;
1072
1073 assert( i != perms->bv_len );
1074
1075 if ( perms->bv_val[ i ] != ',' ) {
1076 Debug( LDAP_DEBUG_ACL, "aciValidatePerms: missing comma in '%s'\n", perms->bv_val );
1077 return LDAP_INVALID_SYNTAX;
1078 }
1079
1080 do {
1081 i++;
1082 } while ( perms->bv_val[ i ] == ' ' );
1083 }
1084
1085 return LDAP_SUCCESS;
1086 }
1087
1088 static const struct berval *ACIgrantdeny[] = {
1089 &aci_bv[ ACI_BV_GRANT ],
1090 &aci_bv[ ACI_BV_DENY ],
1091 NULL
1092 };
1093
1094 static int
OpenLDAPaciValidateRight(struct berval * action)1095 OpenLDAPaciValidateRight(
1096 struct berval *action )
1097 {
1098 struct berval bv = BER_BVNULL;
1099 int i;
1100
1101 /* grant|deny */
1102 if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
1103 bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
1104 {
1105 Debug( LDAP_DEBUG_ACL, "aciValidateRight: '%s' must be either 'grant' or 'deny'\n", bv.bv_val );
1106 return LDAP_INVALID_SYNTAX;
1107 }
1108
1109 for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
1110 if ( i & 1 ) {
1111 /* perms */
1112 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
1113 {
1114 return LDAP_INVALID_SYNTAX;
1115 }
1116
1117 } else {
1118 /* attr */
1119 AttributeDescription *ad;
1120 const char *text;
1121 struct berval attr, left, right;
1122 int j;
1123
1124 /* could be "[all]" or an attribute description */
1125 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
1126 continue;
1127 }
1128
1129
1130 for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ )
1131 {
1132 ad = NULL;
1133 text = NULL;
1134 if ( acl_get_part( &attr, 0, '=', &left ) < 0
1135 || acl_get_part( &attr, 1, '=', &right ) < 0 )
1136 {
1137 if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS )
1138 {
1139 Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", attr.bv_val );
1140 return LDAP_INVALID_SYNTAX;
1141 }
1142 } else {
1143 if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS )
1144 {
1145 Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", left.bv_val );
1146 return LDAP_INVALID_SYNTAX;
1147 }
1148 }
1149 }
1150 }
1151 }
1152
1153 /* "perms;attr" go in pairs */
1154 if ( i > 0 && ( i & 1 ) == 0 ) {
1155 return LDAP_SUCCESS;
1156
1157 } else {
1158 Debug( LDAP_DEBUG_ACL, "aciValidateRight: perms:attr need to be pairs in '%s'\n", action->bv_val );
1159 return LDAP_INVALID_SYNTAX;
1160 }
1161
1162 return LDAP_SUCCESS;
1163 }
1164
1165 static int
OpenLDAPaciNormalizeRight(struct berval * action,struct berval * naction,void * ctx)1166 OpenLDAPaciNormalizeRight(
1167 struct berval *action,
1168 struct berval *naction,
1169 void *ctx )
1170 {
1171 struct berval grantdeny,
1172 perms = BER_BVNULL,
1173 bv = BER_BVNULL;
1174 int idx,
1175 i;
1176
1177 /* grant|deny */
1178 if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
1179 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: missing ';' in '%s'\n", action->bv_val );
1180 return LDAP_INVALID_SYNTAX;
1181 }
1182 idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
1183 if ( idx == -1 ) {
1184 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: '%s' must be grant or deny\n", grantdeny.bv_val );
1185 return LDAP_INVALID_SYNTAX;
1186 }
1187
1188 ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
1189
1190 for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
1191 struct berval nattrs = BER_BVNULL;
1192 int freenattrs = 1;
1193 if ( i & 1 ) {
1194 /* perms */
1195 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
1196 {
1197 return LDAP_INVALID_SYNTAX;
1198 }
1199 perms = bv;
1200
1201 } else {
1202 /* attr */
1203 char *ptr;
1204
1205 /* could be "[all]" or an attribute description */
1206 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
1207 nattrs = aci_bv[ ACI_BV_BR_ALL ];
1208 freenattrs = 0;
1209
1210 } else {
1211 AttributeDescription *ad = NULL;
1212 AttributeDescription adstatic= { 0 };
1213 const char *text = NULL;
1214 struct berval attr, left, right;
1215 int j;
1216 int len;
1217
1218 for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ )
1219 {
1220 ad = NULL;
1221 text = NULL;
1222 /* openldap 2.1 aci compatibility [entry] -> entry */
1223 if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ENTRY ] ) == 0 ) {
1224 ad = &adstatic;
1225 adstatic.ad_cname = aci_bv[ ACI_BV_ENTRY ];
1226
1227 /* openldap 2.1 aci compatibility [children] -> children */
1228 } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_CHILDREN ] ) == 0 ) {
1229 ad = &adstatic;
1230 adstatic.ad_cname = aci_bv[ ACI_BV_CHILDREN ];
1231
1232 /* openldap 2.1 aci compatibility [all] -> only [all] */
1233 } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
1234 ber_memfree_x( nattrs.bv_val, ctx );
1235 nattrs = aci_bv[ ACI_BV_BR_ALL ];
1236 freenattrs = 0;
1237 break;
1238
1239 } else if ( acl_get_part( &attr, 0, '=', &left ) < 0
1240 || acl_get_part( &attr, 1, '=', &right ) < 0 )
1241 {
1242 if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS )
1243 {
1244 ber_memfree_x( nattrs.bv_val, ctx );
1245 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", attr.bv_val );
1246 return LDAP_INVALID_SYNTAX;
1247 }
1248
1249 } else {
1250 if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS )
1251 {
1252 ber_memfree_x( nattrs.bv_val, ctx );
1253 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", left.bv_val );
1254 return LDAP_INVALID_SYNTAX;
1255 }
1256 }
1257
1258
1259 len = nattrs.bv_len + ( !BER_BVISEMPTY( &nattrs ) ? STRLENOF( "," ) : 0 )
1260 + ad->ad_cname.bv_len;
1261 nattrs.bv_val = slap_sl_realloc( nattrs.bv_val, len + 1, ctx );
1262 ptr = &nattrs.bv_val[ nattrs.bv_len ];
1263 if ( !BER_BVISEMPTY( &nattrs ) ) {
1264 *ptr++ = ',';
1265 }
1266 ptr = lutil_strncopy( ptr, ad->ad_cname.bv_val, ad->ad_cname.bv_len );
1267 ptr[ 0 ] = '\0';
1268 nattrs.bv_len = len;
1269 }
1270
1271 }
1272
1273 naction->bv_val = slap_sl_realloc( naction->bv_val,
1274 naction->bv_len + STRLENOF( ";" )
1275 + perms.bv_len + STRLENOF( ";" )
1276 + nattrs.bv_len + 1,
1277 ctx );
1278
1279 ptr = &naction->bv_val[ naction->bv_len ];
1280 ptr[ 0 ] = ';';
1281 ptr++;
1282 ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
1283 ptr[ 0 ] = ';';
1284 ptr++;
1285 ptr = lutil_strncopy( ptr, nattrs.bv_val, nattrs.bv_len );
1286 ptr[ 0 ] = '\0';
1287 naction->bv_len += STRLENOF( ";" ) + perms.bv_len
1288 + STRLENOF( ";" ) + nattrs.bv_len;
1289 if ( freenattrs ) {
1290 ber_memfree_x( nattrs.bv_val, ctx );
1291 }
1292 }
1293 }
1294
1295 /* perms;attr go in pairs */
1296 if ( i > 1 && ( i & 1 ) ) {
1297 return LDAP_SUCCESS;
1298
1299 } else {
1300 Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: perms:attr need to be pairs in '%s'\n", action->bv_val );
1301 return LDAP_INVALID_SYNTAX;
1302 }
1303 }
1304
1305 static int
OpenLDAPaciValidateRights(struct berval * actions)1306 OpenLDAPaciValidateRights(
1307 struct berval *actions )
1308
1309 {
1310 struct berval bv = BER_BVNULL;
1311 int i;
1312
1313 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1314 if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
1315 return LDAP_INVALID_SYNTAX;
1316 }
1317 }
1318
1319 return LDAP_SUCCESS;
1320 }
1321
1322 static int
OpenLDAPaciNormalizeRights(struct berval * actions,struct berval * nactions,void * ctx)1323 OpenLDAPaciNormalizeRights(
1324 struct berval *actions,
1325 struct berval *nactions,
1326 void *ctx )
1327
1328 {
1329 struct berval bv = BER_BVNULL;
1330 int i;
1331
1332 BER_BVZERO( nactions );
1333 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1334 int rc;
1335 struct berval nbv;
1336
1337 rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
1338 if ( rc != LDAP_SUCCESS ) {
1339 ber_memfree_x( nactions->bv_val, ctx );
1340 BER_BVZERO( nactions );
1341 return LDAP_INVALID_SYNTAX;
1342 }
1343
1344 if ( i == 0 ) {
1345 *nactions = nbv;
1346
1347 } else {
1348 nactions->bv_val = slap_sl_realloc( nactions->bv_val,
1349 nactions->bv_len + STRLENOF( "$" )
1350 + nbv.bv_len + 1,
1351 ctx );
1352 nactions->bv_val[ nactions->bv_len ] = '$';
1353 AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
1354 nbv.bv_val, nbv.bv_len + 1 );
1355 ber_memfree_x( nbv.bv_val, ctx );
1356 nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
1357 }
1358 BER_BVZERO( &nbv );
1359 }
1360
1361 return LDAP_SUCCESS;
1362 }
1363
1364 static const struct berval *OpenLDAPaciscopes[] = {
1365 &aci_bv[ ACI_BV_ENTRY ],
1366 &aci_bv[ ACI_BV_CHILDREN ],
1367 &aci_bv[ ACI_BV_SUBTREE ],
1368
1369 NULL
1370 };
1371
1372 static const struct berval *OpenLDAPacitypes[] = {
1373 /* DN-valued */
1374 &aci_bv[ ACI_BV_GROUP ],
1375 &aci_bv[ ACI_BV_ROLE ],
1376
1377 /* set to one past the last DN-valued type with options (/) */
1378 #define LAST_OPTIONAL 2
1379
1380 &aci_bv[ ACI_BV_ACCESS_ID ],
1381 &aci_bv[ ACI_BV_SUBTREE ],
1382 &aci_bv[ ACI_BV_ONELEVEL ],
1383 &aci_bv[ ACI_BV_CHILDREN ],
1384
1385 /* set to one past the last DN-valued type */
1386 #define LAST_DNVALUED 6
1387
1388 /* non DN-valued */
1389 &aci_bv[ ACI_BV_DNATTR ],
1390 &aci_bv[ ACI_BV_PUBLIC ],
1391 &aci_bv[ ACI_BV_USERS ],
1392 &aci_bv[ ACI_BV_SELF ],
1393 &aci_bv[ ACI_BV_SET ],
1394 &aci_bv[ ACI_BV_SET_REF ],
1395
1396 NULL
1397 };
1398
1399 static int
OpenLDAPaciValidate(Syntax * syntax,struct berval * val)1400 OpenLDAPaciValidate(
1401 Syntax *syntax,
1402 struct berval *val )
1403 {
1404 struct berval oid = BER_BVNULL,
1405 scope = BER_BVNULL,
1406 rights = BER_BVNULL,
1407 type = BER_BVNULL,
1408 subject = BER_BVNULL;
1409 int idx;
1410 int rc;
1411
1412 if ( BER_BVISEMPTY( val ) ) {
1413 Debug( LDAP_DEBUG_ACL, "aciValidatet: value is empty\n" );
1414 return LDAP_INVALID_SYNTAX;
1415 }
1416
1417 /* oid */
1418 if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
1419 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1420 {
1421 /* NOTE: the numericoidValidate() is rather pedantic;
1422 * I'd replace it with X-ORDERED VALUES so that
1423 * it's guaranteed values are maintained and used
1424 * in the desired order */
1425 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid oid '%s'\n", oid.bv_val );
1426 return LDAP_INVALID_SYNTAX;
1427 }
1428
1429 /* scope */
1430 if ( acl_get_part( val, 1, '#', &scope ) < 0 ||
1431 bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
1432 {
1433 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid scope '%s'\n", scope.bv_val );
1434 return LDAP_INVALID_SYNTAX;
1435 }
1436
1437 /* rights */
1438 if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
1439 OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS )
1440 {
1441 return LDAP_INVALID_SYNTAX;
1442 }
1443
1444 /* type */
1445 if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1446 Debug( LDAP_DEBUG_ACL, "aciValidate: missing type in '%s'\n", val->bv_val );
1447 return LDAP_INVALID_SYNTAX;
1448 }
1449 idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1450 if ( idx == -1 ) {
1451 struct berval isgr;
1452
1453 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1454 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", type.bv_val );
1455 return LDAP_INVALID_SYNTAX;
1456 }
1457
1458 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1459 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1460 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", isgr.bv_val );
1461 return LDAP_INVALID_SYNTAX;
1462 }
1463 }
1464
1465 /* subject */
1466 bv_get_tail( val, &type, &subject );
1467 if ( subject.bv_val[ 0 ] != '#' ) {
1468 Debug( LDAP_DEBUG_ACL, "aciValidate: missing subject in '%s'\n", val->bv_val );
1469 return LDAP_INVALID_SYNTAX;
1470 }
1471
1472 if ( idx >= LAST_DNVALUED ) {
1473 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1474 AttributeDescription *ad = NULL;
1475 const char *text = NULL;
1476
1477 rc = slap_bv2ad( &subject, &ad, &text );
1478 if ( rc != LDAP_SUCCESS ) {
1479 Debug( LDAP_DEBUG_ACL, "aciValidate: unknown dn attribute '%s'\n", subject.bv_val );
1480 return LDAP_INVALID_SYNTAX;
1481 }
1482
1483 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1484 /* FIXME: allow nameAndOptionalUID? */
1485 Debug( LDAP_DEBUG_ACL, "aciValidate: wrong syntax for dn attribute '%s'\n", subject.bv_val );
1486 return LDAP_INVALID_SYNTAX;
1487 }
1488 }
1489
1490 /* not a DN */
1491 return LDAP_SUCCESS;
1492
1493 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1494 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1495 {
1496 /* do {group|role}/oc/at check */
1497 struct berval ocbv = BER_BVNULL,
1498 atbv = BER_BVNULL;
1499
1500 ocbv.bv_val = ber_bvchr( &type, '/' );
1501 if ( ocbv.bv_val != NULL ) {
1502 ocbv.bv_val++;
1503 ocbv.bv_len = type.bv_len
1504 - ( ocbv.bv_val - type.bv_val );
1505
1506 atbv.bv_val = ber_bvchr( &ocbv, '/' );
1507 if ( atbv.bv_val != NULL ) {
1508 AttributeDescription *ad = NULL;
1509 const char *text = NULL;
1510 int rc;
1511
1512 atbv.bv_val++;
1513 atbv.bv_len = type.bv_len
1514 - ( atbv.bv_val - type.bv_val );
1515 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1516
1517 rc = slap_bv2ad( &atbv, &ad, &text );
1518 if ( rc != LDAP_SUCCESS ) {
1519 Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group attribute '%s'\n", atbv.bv_val );
1520 return LDAP_INVALID_SYNTAX;
1521 }
1522 }
1523
1524 if ( oc_bvfind( &ocbv ) == NULL ) {
1525 Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group '%s'\n", ocbv.bv_val );
1526 return LDAP_INVALID_SYNTAX;
1527 }
1528 }
1529 }
1530
1531 if ( BER_BVISEMPTY( &subject ) ) {
1532 /* empty DN invalid */
1533 Debug( LDAP_DEBUG_ACL, "aciValidate: missing dn in '%s'\n", val->bv_val );
1534 return LDAP_INVALID_SYNTAX;
1535 }
1536
1537 subject.bv_val++;
1538 subject.bv_len--;
1539
1540 /* FIXME: pass DN syntax? */
1541 rc = dnValidate( NULL, &subject );
1542 if ( rc != LDAP_SUCCESS ) {
1543 Debug( LDAP_DEBUG_ACL, "aciValidate: invalid dn '%s'\n", subject.bv_val );
1544 }
1545 return rc;
1546 }
1547
1548 static int
OpenLDAPaciPrettyNormal(struct berval * val,struct berval * out,void * ctx,int normalize)1549 OpenLDAPaciPrettyNormal(
1550 struct berval *val,
1551 struct berval *out,
1552 void *ctx,
1553 int normalize )
1554 {
1555 struct berval oid = BER_BVNULL,
1556 scope = BER_BVNULL,
1557 rights = BER_BVNULL,
1558 nrights = BER_BVNULL,
1559 type = BER_BVNULL,
1560 ntype = BER_BVNULL,
1561 subject = BER_BVNULL,
1562 nsubject = BER_BVNULL;
1563 int idx,
1564 rc = LDAP_SUCCESS,
1565 freesubject = 0,
1566 freetype = 0;
1567 char *ptr;
1568
1569 BER_BVZERO( out );
1570
1571 if ( BER_BVISEMPTY( val ) ) {
1572 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: value is empty\n" );
1573 return LDAP_INVALID_SYNTAX;
1574 }
1575
1576 /* oid: if valid, it's already normalized */
1577 if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
1578 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1579 {
1580 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid oid '%s'\n", oid.bv_val );
1581 return LDAP_INVALID_SYNTAX;
1582 }
1583
1584 /* scope: normalize by replacing with OpenLDAPaciscopes */
1585 if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
1586 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing scope in '%s'\n", val->bv_val );
1587 return LDAP_INVALID_SYNTAX;
1588 }
1589 idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
1590 if ( idx == -1 ) {
1591 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid scope '%s'\n", scope.bv_val );
1592 return LDAP_INVALID_SYNTAX;
1593 }
1594 scope = *OpenLDAPaciscopes[ idx ];
1595
1596 /* rights */
1597 if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
1598 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing rights in '%s'\n", val->bv_val );
1599 return LDAP_INVALID_SYNTAX;
1600 }
1601 if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
1602 != LDAP_SUCCESS )
1603 {
1604 return LDAP_INVALID_SYNTAX;
1605 }
1606
1607 /* type */
1608 if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1609 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing type in '%s'\n", val->bv_val );
1610 rc = LDAP_INVALID_SYNTAX;
1611 goto cleanup;
1612 }
1613 idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1614 if ( idx == -1 ) {
1615 struct berval isgr;
1616
1617 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1618 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", type.bv_val );
1619 rc = LDAP_INVALID_SYNTAX;
1620 goto cleanup;
1621 }
1622
1623 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1624 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1625 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", isgr.bv_val );
1626 rc = LDAP_INVALID_SYNTAX;
1627 goto cleanup;
1628 }
1629 }
1630 ntype = *OpenLDAPacitypes[ idx ];
1631
1632 /* subject */
1633 bv_get_tail( val, &type, &subject );
1634
1635 if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
1636 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing subject in '%s'\n", val->bv_val );
1637 rc = LDAP_INVALID_SYNTAX;
1638 goto cleanup;
1639 }
1640
1641 subject.bv_val++;
1642 subject.bv_len--;
1643
1644 if ( idx < LAST_DNVALUED ) {
1645 /* FIXME: pass DN syntax? */
1646 if ( normalize ) {
1647 rc = dnNormalize( 0, NULL, NULL,
1648 &subject, &nsubject, ctx );
1649 } else {
1650 rc = dnPretty( NULL, &subject, &nsubject, ctx );
1651 }
1652
1653 if ( rc == LDAP_SUCCESS ) {
1654 freesubject = 1;
1655
1656 } else {
1657 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid subject dn '%s'\n", subject.bv_val );
1658 goto cleanup;
1659 }
1660
1661 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1662 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1663 {
1664 /* do {group|role}/oc/at check */
1665 struct berval ocbv = BER_BVNULL,
1666 atbv = BER_BVNULL;
1667
1668 ocbv.bv_val = ber_bvchr( &type, '/' );
1669 if ( ocbv.bv_val != NULL ) {
1670 ObjectClass *oc = NULL;
1671 AttributeDescription *ad = NULL;
1672 const char *text = NULL;
1673 int rc;
1674 struct berval bv;
1675
1676 bv.bv_len = ntype.bv_len;
1677
1678 ocbv.bv_val++;
1679 ocbv.bv_len = type.bv_len - ( ocbv.bv_val - type.bv_val );
1680
1681 atbv.bv_val = ber_bvchr( &ocbv, '/' );
1682 if ( atbv.bv_val != NULL ) {
1683 atbv.bv_val++;
1684 atbv.bv_len = type.bv_len
1685 - ( atbv.bv_val - type.bv_val );
1686 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1687
1688 rc = slap_bv2ad( &atbv, &ad, &text );
1689 if ( rc != LDAP_SUCCESS ) {
1690 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown group attribute '%s'\n", atbv.bv_val );
1691 rc = LDAP_INVALID_SYNTAX;
1692 goto cleanup;
1693 }
1694
1695 bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
1696 }
1697
1698 oc = oc_bvfind( &ocbv );
1699 if ( oc == NULL ) {
1700 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid group '%s'\n", ocbv.bv_val );
1701 rc = LDAP_INVALID_SYNTAX;
1702 goto cleanup;
1703 }
1704
1705 bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
1706 bv.bv_val = slap_sl_malloc( bv.bv_len + 1, ctx );
1707
1708 ptr = bv.bv_val;
1709 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1710 ptr[ 0 ] = '/';
1711 ptr++;
1712 ptr = lutil_strncopy( ptr,
1713 oc->soc_cname.bv_val,
1714 oc->soc_cname.bv_len );
1715 if ( ad != NULL ) {
1716 ptr[ 0 ] = '/';
1717 ptr++;
1718 ptr = lutil_strncopy( ptr,
1719 ad->ad_cname.bv_val,
1720 ad->ad_cname.bv_len );
1721 }
1722 ptr[ 0 ] = '\0';
1723
1724 ntype = bv;
1725 freetype = 1;
1726 }
1727 }
1728
1729 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1730 AttributeDescription *ad = NULL;
1731 const char *text = NULL;
1732 int rc;
1733
1734 rc = slap_bv2ad( &subject, &ad, &text );
1735 if ( rc != LDAP_SUCCESS ) {
1736 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown dn attribute '%s'\n", subject.bv_val );
1737 rc = LDAP_INVALID_SYNTAX;
1738 goto cleanup;
1739 }
1740
1741 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1742 /* FIXME: allow nameAndOptionalUID? */
1743 Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: wrong syntax for dn attribute '%s'\n", subject.bv_val );
1744 rc = LDAP_INVALID_SYNTAX;
1745 goto cleanup;
1746 }
1747
1748 nsubject = ad->ad_cname;
1749
1750 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_SET ]
1751 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_SET_REF ] )
1752 {
1753 /* NOTE: dunno how to normalize it... */
1754 nsubject = subject;
1755 }
1756
1757
1758 out->bv_len =
1759 oid.bv_len + STRLENOF( "#" )
1760 + scope.bv_len + STRLENOF( "#" )
1761 + nrights.bv_len + STRLENOF( "#" )
1762 + ntype.bv_len + STRLENOF( "#" )
1763 + nsubject.bv_len;
1764
1765 out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
1766 ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
1767 ptr[ 0 ] = '#';
1768 ptr++;
1769 ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
1770 ptr[ 0 ] = '#';
1771 ptr++;
1772 ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
1773 ptr[ 0 ] = '#';
1774 ptr++;
1775 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1776 ptr[ 0 ] = '#';
1777 ptr++;
1778 if ( !BER_BVISNULL( &nsubject ) ) {
1779 ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
1780 }
1781 ptr[ 0 ] = '\0';
1782
1783 cleanup:;
1784 if ( freesubject ) {
1785 ber_memfree_x( nsubject.bv_val, ctx );
1786 }
1787
1788 if ( freetype ) {
1789 ber_memfree_x( ntype.bv_val, ctx );
1790 }
1791
1792 if ( !BER_BVISNULL( &nrights ) ) {
1793 ber_memfree_x( nrights.bv_val, ctx );
1794 }
1795
1796 return rc;
1797 }
1798
1799 static int
OpenLDAPaciPretty(Syntax * syntax,struct berval * val,struct berval * out,void * ctx)1800 OpenLDAPaciPretty(
1801 Syntax *syntax,
1802 struct berval *val,
1803 struct berval *out,
1804 void *ctx )
1805 {
1806 return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
1807 }
1808
1809 static int
OpenLDAPaciNormalize(slap_mask_t use,Syntax * syntax,MatchingRule * mr,struct berval * val,struct berval * out,void * ctx)1810 OpenLDAPaciNormalize(
1811 slap_mask_t use,
1812 Syntax *syntax,
1813 MatchingRule *mr,
1814 struct berval *val,
1815 struct berval *out,
1816 void *ctx )
1817 {
1818 return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
1819 }
1820
1821 #if SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC
1822 /*
1823 * FIXME: need config and Makefile.am code to ease building
1824 * as dynamic module
1825 */
1826 int
init_module(int argc,char * argv[])1827 init_module( int argc, char *argv[] )
1828 {
1829 return dynacl_aci_init();
1830 }
1831 #endif /* SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC */
1832
1833 #endif /* SLAPD_ACI_ENABLED */
1834
1835