]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/alias.c
Round two of referrals/aliases: WORK IN PROGRESS, may not compile!
[openldap] / servers / slapd / back-ldbm / alias.c
1 /*
2  * Copyright (c) 1998 Will Ballantyne, ITSD, Government of BC
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to ITSD, Government of BC. The name of ITSD
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12
13 #include "portable.h"
14
15 #include <stdio.h>
16 #include <ac/string.h>
17 #include <ac/socket.h>
18 #include "slap.h"
19 #include "back-ldbm.h"
20 #include "proto-back-ldbm.h"
21
22 #ifdef SLAPD_ALIASES
23
24 /*
25  * dereference alias
26  *      input origEntry is should be locked/unlocked by caller.
27  *
28  * returns origEntry if origEntry is not an alias
29  * returns NULL if error
30  * otherwise returns read locked alias
31  */
32 Entry *deref_alias_r (
33         Backend         *be,
34         Connection      *conn,
35         Operation       *op,
36         Entry           *origEntry,
37         int                     *err,
38         char            **matched_dn
39 )
40 {
41         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
42         unsigned depth;
43         Entry *e;
44         char **aliases = NULL;
45         char *newDN = NULL;
46         char *oldDN = NULL;
47         int rc = LDAP_SUCCESS;
48
49         /*
50          * Aliases are only deref'ed during search operations.
51          * if deref_alias_r (or deref_dn) is needed by other op,
52          * this will need to become argument
53          */
54         const int access = ACL_SEARCH;
55
56         /* be sure we have a starting entry */
57         if( origEntry != NULL ) {
58                 return NULL;
59         }
60
61         Debug( LDAP_DEBUG_TRACE, "<= checking for alias for dn %s\n",
62                 origEntry->e_dn, 0, 0 );
63
64         /*
65          * try to deref fully, up to a maximum depth.   If the max depth exceeded
66          * then send an error
67          */
68         e = origEntry;
69         for ( depth = 0; e != NULL; depth++ ) 
70         {
71                 Attribute *a;
72                 struct berval bv;
73
74                 if ( ! access_allowed( be, conn, op, e,
75                         "entry", NULL, access ) )
76                 {
77                         Debug( LDAP_DEBUG_ACL,
78                                 "deref_alias_r: access to entry not allowed\n",
79                                 0, 0, 0 );
80                         break;
81                 }
82
83                 /*
84                  * aliased object names must be contained in an entry
85                  * object class "alias".
86                  */
87                 a = attr_find(e->e_attrs, "objectclass");
88
89                 if( a == NULL ) {
90                         /* no objectclass attribute */
91                         break;
92                 }
93
94                 bv.bv_val = "REFERRAL";
95                 bv.bv_len = sizeof("REFERRAL")-1;
96         
97                 if (value_find(a->a_vals, &bv, a->a_syntax, 1) == 0) {
98                         /* is a referral */
99                         break;
100                 }
101
102                 bv.bv_val = "ALIAS";
103                 bv.bv_len = sizeof("ALIAS")-1;
104         
105                 if (value_find(a->a_vals, &bv, a->a_syntax, 1) != 0) {
106                         /* not an alias */
107                         break;
108                 }
109
110                 if ( ! access_allowed( be, conn, op, e,
111                         "aliasedobjectname", NULL, access ) )
112                 {
113                         Debug( LDAP_DEBUG_ACL,
114                                 "deref_alias_r: access to reference not allowed\n",
115                                 0, 0, 0 );
116                         break;
117                 }
118
119                 a = attr_find( e->e_attrs, "aliasedobjectname" );
120
121                 if( a == NULL ) {
122                         /*
123                          * there was an aliasedobjectname defined but no data.
124                          */
125                         Debug( LDAP_DEBUG_TRACE, 
126                                  "<= %s has no aliasedObjectName attribute\n", 
127                                  e->e_dn, 0, 0 );
128                         send_ldap_result( conn, op, rc = LDAP_ALIAS_PROBLEM,
129                                 NULL, "alias missing aliasedObjectName", NULL, NULL );
130                         break;
131                 }
132
133                 /* 
134                  * aliasedObjectName should be SINGLE-VALUED with a single value. 
135                  */                     
136                 if ( a->a_vals[0] == NULL || a->a_vals[0]->bv_val != NULL ) {
137                         /*
138                          * there was an aliasedobjectname defined but no data.
139                          */
140                         Debug( LDAP_DEBUG_TRACE, 
141                                  "<= %s has no value  aliasedObjectName attribute\n", 
142                                  e->e_dn, 0, 0 );
143                         send_ldap_result( conn, op, rc = LDAP_ALIAS_PROBLEM,
144                                 NULL, "alias missing aliasedObjectName value", NULL, NULL );
145                         break;
146                 }
147
148                 if( a->a_vals[1] != NULL ) {
149                         Debug( LDAP_DEBUG_TRACE, 
150                                  "<= %s alias has multiple values\n", 
151                                  e->e_dn, 0, 0 );
152                         send_ldap_result( conn, op, rc= LDAP_ALIAS_PROBLEM,
153                                 NULL, "multivalue aliasObjectName", NULL, NULL );
154                         break;
155                 }
156
157                 if( depth >= be->be_max_deref_depth ) {
158                         /* depth limit exceeded */
159                         Debug( LDAP_DEBUG_TRACE, 
160                                  "<= deref(\"%s\") exceeded maximum deref depth (%d) at \"%s\"\n", 
161                                  origEntry->e_dn, 
162                                  be->be_max_deref_depth, 
163                                  e->e_ndn );
164                         send_ldap_result( conn, op, rc = LDAP_ALIAS_DEREF_PROBLEM,
165                                 NULL, "maximum deref depth exceeded", NULL, NULL );
166                         break;
167                 }
168
169                 charray_add( &aliases, e->e_ndn );
170
171                 Debug( LDAP_DEBUG_TRACE, "<= %s is an alias for %s\n", 
172                         e->e_dn, a->a_vals[0]->bv_val, 0 );
173
174                 if( oldDN != NULL ) free( oldDN );
175                 oldDN = ch_strdup( e->e_ndn );
176
177                 /* 
178                  * release past lock if not original
179                  */
180                 if ( depth > 0 ) {
181                         cache_return_entry_r(&li->li_cache, e);
182                 }
183                 e = NULL;
184
185                 if( newDN != NULL ) free( newDN );
186                 newDN = ch_strdup( a->a_vals[0]->bv_val );
187                 dn_normalize_case (newDN);
188
189                 /* make sure new and old DN are not same to avoid loops */
190                 if ( charray_inlist( aliases, newDN ) ) {
191                         Debug( LDAP_DEBUG_TRACE, 
192                                  "<= %s has circular alias %s\n", 
193                                  origEntry->e_dn, newDN, 0 );
194                         send_ldap_result( conn, op, rc = LDAP_LOOP_DETECT,
195                                 NULL, "circular alias", NULL, NULL );
196                         break;
197                 }
198
199                 /*
200                  * ok, so what happens if there is an alias in the DN of a dereferenced
201                  * alias object?        
202                  */
203                 if ( (e = dn2entry_r( be, newDN, NULL )) == NULL ) {
204                         /* could not deref return error */
205                         Debug( LDAP_DEBUG_TRACE, 
206                                  "<= %s has dangling alias %s to %s\n", 
207                                  origEntry->e_dn, oldDN, newDN );
208                         send_ldap_result( conn, op, rc = LDAP_ALIAS_DEREF_PROBLEM,
209                                 NULL, "dangling alias", NULL, NULL );
210                         break;
211                 }
212         }
213
214         if( e != NULL && origEntry != e && rc != LDAP_SUCCESS ) {
215                 cache_return_entry_r(&li->li_cache, e);
216                 e = NULL;
217         }
218
219         charray_free( aliases );
220         if( newDN ) free(newDN);
221         if( oldDN ) free(oldDN);
222
223         return e;
224 }
225
226
227 /*
228  * given a DN fully deref it and return the real DN or original DN if it fails
229  * This involves finding the last matched part then reconstructing forward.
230  *
231  * Example:
232  *
233  * "cn=AliasUser,ou=OU,o=AliasedOrg,c=CA" where
234  *              "o=AliasedOrg,c=CA" is an alias for
235  *                     "o=Org,c=CA"
236  *      and
237  *              "cn=AliasUser,ou=OU,o=Org,c=CA" is an alias for
238  *                   "cn=User,ou=OU,o=Org,c=CA"
239  *
240  * 1) newDN = dn
241  *              newDN is "cn=AliasUser,ou=OU,o=AliasedOrg,c=CA"
242  *
243  * 2) loop: e = d2entry_r( newDN, matched )
244  *              e is NULL
245  *              matched is entry("o=AliasOrg,c=CA")
246  *
247  * 3) rmdr = remainder(newDN, matched)
248  *              rmdr is "cn=AliasUser,ou=OU"
249  *
250  * 4) alias = deref(matched)
251  *              alias is entry("o=Org,c=CA")
252  *
253  * 5) oldDN=newDN; newDN = rmdr + alias
254  *              oldDN is "cn=AliasUser,ou=OU,o=AliasedOrg,c=CA"
255  *              newDN is "cn=AliasUser,ou=OU,o=Org,c=CA"
256  *
257  * 6) compare(oldDN,newDN)
258  *              goto loop (step 2)
259  *
260  * 7) e = d2entry_r( newDN, matched )
261  *              e is NULL
262  *              matched is entry("ou=OU,o=Org,c=CA")
263  *
264  * 8) rmdr = remainder(newDN, matched)
265  *              rmdr is "cn=AliasUser"
266  *
267  * 9) alias = deref(matched)
268  *              alias is entry("ou=OU,o=Org,c=CA")
269  *
270  *10) oldDN=newDN; newDN = rmdr + alias
271  *              oldDN is "cn=AliasUser,ou=OU,o=Org,c=CA"
272  *              newDN is "cn=AliasUser,ou=OU,o=Org,c=CA"
273  *
274  *11) compare(oldDN,newDN)
275  *              break loop (step 2)
276  *
277  *12) return newDN
278  *
279  */
280 char *deref_dn (
281         Backend         *be,
282         Connection      *conn,
283         Operation       *op,
284         char            *dn
285 )
286 {
287         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
288         unsigned        depth;
289         char*   remainder = NULL;
290         char*   newDN;
291
292         char    **dns;
293         
294         if (!dn) return NULL; 
295
296         Debug( LDAP_DEBUG_TRACE, 
297                 "<= dereferencing dn: \"%s\"\n", 
298                 dn, 0, 0 );
299
300         charray_add( &dns, "" );
301
302         newDN = ch_strdup( dn );
303
304         for ( depth = 0; charray_inlist( dns, newDN ) != 0; depth++ )
305         {
306                 Entry*  e = NULL;
307                 Entry*  matched = NULL;
308                 Entry*  alias = NULL;
309                 int     rlen;
310
311                 if( depth >= be->be_max_deref_depth ) {
312                         /* depth limit exceeded */
313                         break;
314                 }
315
316                 e = dn2entry_r( be, newDN, &matched );
317                 
318                 if( e != NULL ) {
319                         cache_return_entry_r(&li->li_cache, e);
320                         break;
321                 }
322
323                 if ( matched == NULL ) {
324                         /* nothing matched */
325                         break;
326                 }
327
328                 charray_add( &dns, newDN );
329
330                 Debug( LDAP_DEBUG_TRACE, "<= matched %s\n", matched->e_dn, 0, 0 );
331
332                 rlen = strlen( newDN ) - strlen( matched->e_ndn );
333                 remainder = ch_malloc( rlen + 1 );
334                 strncpy( remainder, newDN, rlen );
335                 remainder[rlen] = '\0';
336         
337                 Debug( LDAP_DEBUG_TRACE, "<= remainder %s\n", remainder, 0, 0 );
338
339                 alias = deref_alias_r( be, conn, op, matched );
340
341                 cache_return_entry_r(&li->li_cache, matched);
342
343                 if( alias == matched ) {
344                         /* matched isn't an alias */
345                         break;
346                 }
347
348                 if( alias == NULL )  {
349                         /* alias error */
350                         break;
351                 }
352         
353                 Debug( LDAP_DEBUG_TRACE, "<= derefenced to %s\n", alias->e_dn, 0, 0 );
354
355                 free( newDN );
356                 newDN = ch_malloc( rlen + strlen( alias->e_ndn ) + 1 );
357                 sprintf("%s%s", remainder, alias->e_ndn );
358
359                 free( remainder );
360                 remainder = NULL;
361
362                 Debug( LDAP_DEBUG_TRACE, "<= expanded to %s\n", newDN, 0, 0 );
363
364                 cache_return_entry_r( &li->li_cache, alias );
365         }
366
367         charray_free( dns );
368
369         if( remainder != NULL ) {
370                 free( remainder );
371         }
372
373         Debug( LDAP_DEBUG_TRACE, "<= %s\n", newDN, 0, 0 );
374
375         return newDN;
376 }
377 #endif