]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/allowed/allowed.c
Happy New Year!
[openldap] / contrib / slapd-modules / allowed / allowed.c
1 /* allowed.c - add allowed attributes based on ACL */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2006-2016 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 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Pierangelo Masarati for inclusion in
18  * OpenLDAP Software.
19  */
20
21 /*
22  * Rationale: return in allowedAttributes the attributes required/allowed
23  * by the objectClasses that are currently present in an object; return
24  * in allowedAttributesEffective the subset of the above that can be written
25  * by the identity that performs the search.
26  *
27  * Caveats:
28  * - right now, the overlay assumes that all values of the objectClass
29  *   attribute will be returned in rs->sr_entry; this may not be true
30  *   in general, but it usually is for back-bdb/back-hdb.  To generalize,
31  *   the search request should be analyzed, and if allowedAttributes or
32  *   allowedAttributesEffective are requested, add objectClass to the
33  *   requested attributes
34  * - it assumes that there is no difference between write-add and 
35  *   write-delete
36  * - it assumes that access rules do not depend on the values of the 
37  *   attributes or on the contents of the entry (attr/val, filter, ...)
38  *   allowedAttributes and allowedAttributesEffective cannot be used
39  *   in filters or in compare
40  */
41
42 #include "portable.h"
43
44 /* define SLAPD_OVER_ALLOWED=2 to build as run-time loadable module */
45 #ifdef SLAPD_OVER_ALLOWED
46
47 #include "slap.h"
48
49 /*
50  * NOTE: part of the schema definition reported below is taken
51  * from Microsoft schema definitions (OID, NAME, SYNTAX);
52  *
53  * EQUALITY is taken from
54  * <http://www.redhat.com/archives/fedora-directory-devel/2006-August/msg00007.html>
55  * (posted by Andrew Bartlett)
56  *
57  * The rest is guessed.  Specifically
58  *
59  * DESC briefly describes the purpose
60  *
61  * NO-USER-MODIFICATION is added to make attributes operational
62  *
63  * USAGE is set to "dSAOperation" as per ITS#7493,
64  * to prevent replication, since this information
65  * is generated (based on ACL and identity of request)
66  * and not stored.
67  */
68
69 #define AA_SCHEMA_AT "1.2.840.113556.1.4"
70
71 static AttributeDescription
72                 *ad_allowedChildClasses,
73                 *ad_allowedChildClassesEffective,
74                 *ad_allowedAttributes,
75                 *ad_allowedAttributesEffective;
76
77 static struct {
78         char *at;
79         AttributeDescription **ad;
80 } aa_attrs[] = {
81         { "( " AA_SCHEMA_AT ".911 "
82                 "NAME 'allowedChildClasses' "
83                 "EQUALITY objectIdentifierMatch "
84                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
85                 /* added by me :) */
86                 "DESC 'Child classes allowed for a given object' "
87                 "NO-USER-MODIFICATION "
88                 "USAGE dSAOperation )", &ad_allowedChildClasses },
89         { "( " AA_SCHEMA_AT ".912 "
90                 "NAME 'allowedChildClassesEffective' "
91                 "EQUALITY objectIdentifierMatch "
92                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
93                 /* added by me :) */
94                 "DESC 'Child classes allowed for a given object according to ACLs' "
95                 "NO-USER-MODIFICATION "
96                 "USAGE dSAOperation )", &ad_allowedChildClassesEffective },
97         { "( " AA_SCHEMA_AT ".913 "
98                 "NAME 'allowedAttributes' "
99                 "EQUALITY objectIdentifierMatch "
100                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
101                 /* added by me :) */
102                 "DESC 'Attributes allowed for a given object' "
103                 "NO-USER-MODIFICATION "
104                 "USAGE dSAOperation )", &ad_allowedAttributes },
105         { "( " AA_SCHEMA_AT ".914 "
106                 "NAME 'allowedAttributesEffective' "
107                 "EQUALITY objectIdentifierMatch "
108                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
109                 /* added by me :) */
110                 "DESC 'Attributes allowed for a given object according to ACLs' "
111                 "NO-USER-MODIFICATION "
112                 "USAGE dSAOperation )", &ad_allowedAttributesEffective },
113
114         /* TODO: add objectClass stuff? */
115
116         { NULL, NULL }
117 };
118
119 static int
120 aa_add_at( AttributeType *at, AttributeType ***atpp )
121 {
122         int             i = 0;
123
124         if ( *atpp ) {
125                 for ( i = 0; (*atpp)[ i ] != NULL; i++ ) {
126                         if ( (*atpp)[ i ] == at ) {
127                                 break;
128                         }
129                 }
130         
131                 if ( (*atpp)[ i ] != NULL ) {
132                         return 0;
133                 }
134         }
135
136         *atpp = ch_realloc( *atpp, sizeof( AttributeType * ) * ( i + 2 ) );
137         (*atpp)[ i ] = at;
138         (*atpp)[ i + 1 ] = NULL;
139
140         return 0;
141 }
142
143 static int
144 aa_add_oc( ObjectClass *oc, ObjectClass ***ocpp, AttributeType ***atpp )
145 {
146         int             i = 0;
147
148         if ( *ocpp ) {
149                 for ( ; (*ocpp)[ i ] != NULL; i++ ) {
150                         if ( (*ocpp)[ i ] == oc ) {
151                                 break;
152                         }
153                 }
154
155                 if ( (*ocpp)[ i ] != NULL ) {
156                         return 0;
157                 }
158         }
159
160         *ocpp = ch_realloc( *ocpp, sizeof( ObjectClass * ) * ( i + 2 ) );
161         (*ocpp)[ i ] = oc;
162         (*ocpp)[ i + 1 ] = NULL;
163
164         if ( oc->soc_required ) {
165                 int             i;
166
167                 for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) {
168                         aa_add_at( oc->soc_required[ i ], atpp );
169                 }
170         }
171
172         if ( oc->soc_allowed ) {
173                 int             i;
174
175                 for ( i = 0; oc->soc_allowed[ i ] != NULL; i++ ) {
176                         aa_add_at( oc->soc_allowed[ i ], atpp );
177                 }
178         }
179
180         return 0;
181 }
182
183 static int
184 aa_operational( Operation *op, SlapReply *rs )
185 {
186         Attribute               *a, **ap;
187         AccessControlState      acl_state = ACL_STATE_INIT;
188         struct berval           *v;
189         AttributeType           **atp = NULL;
190         ObjectClass             **ocp = NULL;
191
192 #define GOT_NONE        (0x0U)
193 #define GOT_C           (0x1U)
194 #define GOT_CE          (0x2U)
195 #define GOT_A           (0x4U)
196 #define GOT_AE          (0x8U)
197 #define GOT_ALL         (GOT_C|GOT_CE|GOT_A|GOT_AE)
198         int             got = GOT_NONE;
199
200         /* only add if requested */
201         if ( SLAP_OPATTRS( rs->sr_attr_flags ) ) {
202                 got = GOT_ALL;
203
204         } else {
205                 if ( ad_inlist( ad_allowedChildClasses, rs->sr_attrs ) ) {
206                         got |= GOT_C;
207                 }
208
209                 if ( ad_inlist( ad_allowedChildClassesEffective, rs->sr_attrs ) ) {
210                         got |= GOT_CE;
211                 }
212
213                 if ( ad_inlist( ad_allowedAttributes, rs->sr_attrs ) ) {
214                         got |= GOT_A;
215                 }
216
217                 if ( ad_inlist( ad_allowedAttributesEffective, rs->sr_attrs ) ) {
218                         got |= GOT_AE;
219                 }
220         }
221
222         if ( got == GOT_NONE ) {
223                 return SLAP_CB_CONTINUE;
224         }
225
226         /* shouldn't be called without an entry; please check */
227         assert( rs->sr_entry != NULL );
228
229         for ( ap = &rs->sr_operational_attrs; *ap != NULL; ap = &(*ap)->a_next )
230                 /* go to last */ ;
231
232         /* see caveats; this is not guaranteed for all backends */
233         a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_objectClass );
234         if ( a == NULL ) {
235                 goto do_oc;
236         }
237
238         /* if client has no access to objectClass attribute; don't compute */
239         if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_objectClass,
240                                 NULL, ACL_READ, &acl_state ) )
241         {
242                 return SLAP_CB_CONTINUE;
243         }
244
245         for ( v = a->a_nvals; !BER_BVISNULL( v ); v++ ) {
246                 ObjectClass     *oc = oc_bvfind( v );
247
248                 assert( oc != NULL );
249
250                 /* if client has no access to specific value, don't compute */
251                 if ( !access_allowed( op, rs->sr_entry,
252                         slap_schema.si_ad_objectClass,
253                         &oc->soc_cname, ACL_READ, &acl_state ) )
254                 {
255                         continue;
256                 }
257
258                 aa_add_oc( oc, &ocp, &atp );
259
260                 if ( oc->soc_sups ) {
261                         int i;
262
263                         for ( i = 0; oc->soc_sups[ i ] != NULL; i++ ) {
264                                 aa_add_oc( oc->soc_sups[ i ], &ocp, &atp );
265                         }
266                 }
267         }
268
269         ch_free( ocp );
270
271         if ( atp != NULL ) {
272                 BerVarray       bv_allowed = NULL,
273                                 bv_effective = NULL;
274                 int             i, ja = 0, je = 0;
275
276                 for ( i = 0; atp[ i ] != NULL; i++ )
277                         /* just count */ ;
278         
279                 if ( got & GOT_A ) {
280                         bv_allowed = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) );
281                 }
282                 if ( got & GOT_AE ) {
283                         bv_effective = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) );
284                 }
285
286                 for ( i = 0, ja = 0, je = 0; atp[ i ] != NULL; i++ ) {
287                         if ( got & GOT_A ) {
288                                 ber_dupbv( &bv_allowed[ ja ], &atp[ i ]->sat_cname );
289                                 ja++;
290                         }
291
292                         if ( got & GOT_AE ) {
293                                 AttributeDescription    *ad = NULL;
294                                 const char              *text = NULL;
295         
296                                 if ( slap_bv2ad( &atp[ i ]->sat_cname, &ad, &text ) ) {
297                                         /* log? */
298                                         continue;
299                                 }
300
301                                 if ( access_allowed( op, rs->sr_entry,
302                                         ad, NULL, ACL_WRITE, NULL ) )
303                                 {
304                                         ber_dupbv( &bv_effective[ je ], &atp[ i ]->sat_cname );
305                                         je++;
306                                 }
307                         }
308                 }
309
310                 ch_free( atp );
311
312                 if ( ( got & GOT_A ) && ja > 0 ) {
313                         BER_BVZERO( &bv_allowed[ ja ] );
314                         *ap = attr_alloc( ad_allowedAttributes );
315                         (*ap)->a_vals = bv_allowed;
316                         (*ap)->a_nvals = bv_allowed;
317                         (*ap)->a_numvals = ja;
318                         ap = &(*ap)->a_next;
319                 }
320
321                 if ( ( got & GOT_AE ) && je > 0 ) {
322                         BER_BVZERO( &bv_effective[ je ] );
323                         *ap = attr_alloc( ad_allowedAttributesEffective );
324                         (*ap)->a_vals = bv_effective;
325                         (*ap)->a_nvals = bv_effective;
326                         (*ap)->a_numvals = je;
327                         ap = &(*ap)->a_next;
328                 }
329
330                 *ap = NULL;
331         }
332
333 do_oc:;
334         if ( ( got & GOT_C ) || ( got & GOT_CE ) ) {
335                 BerVarray       bv_allowed = NULL,
336                                 bv_effective = NULL;
337                 int             i, ja = 0, je = 0;
338
339                 ObjectClass     *oc;
340
341                 for ( oc_start( &oc ); oc != NULL; oc_next( &oc ) ) {
342                         /* we can only add AUXILIARY objectClasses */
343                         if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
344                                 continue;
345                         }
346
347                         i++;
348                 }
349
350                 if ( got & GOT_C ) {
351                         bv_allowed = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) );
352                 }
353                 if ( got & GOT_CE ) {
354                         bv_effective = ber_memalloc( sizeof( struct berval ) * ( i + 1 ) );
355                 }
356
357                 for ( oc_start( &oc ); oc != NULL; oc_next( &oc ) ) {
358                         /* we can only add AUXILIARY objectClasses */
359                         if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
360                                 continue;
361                         }
362
363                         if ( got & GOT_C ) {
364                                 ber_dupbv( &bv_allowed[ ja ], &oc->soc_cname );
365                                 ja++;
366                         }
367
368                         if ( got & GOT_CE ) {
369                                 if ( !access_allowed( op, rs->sr_entry,
370                                         slap_schema.si_ad_objectClass,
371                                         &oc->soc_cname, ACL_WRITE, NULL ) )
372                                 {
373                                         goto done_ce;
374                                 }
375
376                                 if ( oc->soc_required ) {
377                                         for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) {
378                                                 AttributeDescription    *ad = NULL;
379                                                 const char              *text = NULL;
380         
381                                                 if ( slap_bv2ad( &oc->soc_required[ i ]->sat_cname, &ad, &text ) ) {
382                                                         /* log? */
383                                                         continue;
384                                                 }
385
386                                                 if ( !access_allowed( op, rs->sr_entry,
387                                                         ad, NULL, ACL_WRITE, NULL ) )
388                                                 {
389                                                         goto done_ce;
390                                                 }
391                                         }
392                                 }
393
394                                 ber_dupbv( &bv_effective[ je ], &oc->soc_cname );
395                                 je++;
396                         }
397 done_ce:;
398                 }
399
400                 if ( ( got & GOT_C ) && ja > 0 ) {
401                         BER_BVZERO( &bv_allowed[ ja ] );
402                         *ap = attr_alloc( ad_allowedChildClasses );
403                         (*ap)->a_vals = bv_allowed;
404                         (*ap)->a_nvals = bv_allowed;
405                         (*ap)->a_numvals = ja;
406                         ap = &(*ap)->a_next;
407                 }
408
409                 if ( ( got & GOT_CE ) && je > 0 ) {
410                         BER_BVZERO( &bv_effective[ je ] );
411                         *ap = attr_alloc( ad_allowedChildClassesEffective );
412                         (*ap)->a_vals = bv_effective;
413                         (*ap)->a_nvals = bv_effective;
414                         (*ap)->a_numvals = je;
415                         ap = &(*ap)->a_next;
416                 }
417
418                 *ap = NULL;
419         }
420
421         return SLAP_CB_CONTINUE;
422 }
423
424 static slap_overinst aa;
425
426 #if LDAP_VENDOR_VERSION_MINOR != X && LDAP_VENDOR_VERSION_MINOR <= 3
427 /* backport register_at() from HEAD, to allow building with OL <= 2.3 */
428 static int
429 register_at( char *def, AttributeDescription **rad, int dupok )
430 {
431         LDAPAttributeType *at;
432         int code, freeit = 0;
433         const char *err;
434         AttributeDescription *ad = NULL;
435
436         at = ldap_str2attributetype( def, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
437         if ( !at ) {
438                 Debug( LDAP_DEBUG_ANY,
439                         "register_at: AttributeType \"%s\": %s, %s\n",
440                                 def, ldap_scherr2str(code), err );
441                 return code;
442         }
443
444         code = at_add( at, 0, NULL, &err );
445         if ( code ) {
446                 if ( code == SLAP_SCHERR_ATTR_DUP && dupok ) {
447                         freeit = 1;
448
449                 } else {
450                         ldap_attributetype_free( at );
451                         Debug( LDAP_DEBUG_ANY,
452                                 "register_at: AttributeType \"%s\": %s, %s\n",
453                                 def, scherr2str(code), err );
454                         return code;
455                 }
456         }
457         code = slap_str2ad( at->at_names[0], &ad, &err );
458         if ( freeit || code ) {
459                 ldap_attributetype_free( at );
460         } else {
461                 ldap_memfree( at );
462         }
463         if ( code ) {
464                 Debug( LDAP_DEBUG_ANY, "register_at: AttributeType \"%s\": %s\n",
465                         def, err, 0 );
466         }
467         if ( rad ) *rad = ad;
468         return code;
469 }
470 #endif
471
472 #if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC
473 static
474 #endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */
475 int
476 aa_initialize( void )
477 {
478         int i;
479
480         aa.on_bi.bi_type = "allowed";
481
482         aa.on_bi.bi_operational = aa_operational;
483
484         /* aa schema integration */
485         for ( i = 0; aa_attrs[i].at; i++ ) {
486                 int code;
487
488                 code = register_at( aa_attrs[i].at, aa_attrs[i].ad, 0 );
489                 if ( code ) {
490                         Debug( LDAP_DEBUG_ANY,
491                                 "aa_initialize: register_at failed\n", 0, 0, 0 );
492                         return -1;
493                 }
494         }
495
496         return overlay_register( &aa );
497 }
498
499 #if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC
500 int
501 init_module( int argc, char *argv[] )
502 {
503         return aa_initialize();
504 }
505 #endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */
506
507 #endif /* SLAPD_OVER_ALLOWED */