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