]> git.sur5r.net Git - openldap/blob - servers/slapd/backend.c
309f26c2adaba3da84bc22fbdfa0b487da783d6a
[openldap] / servers / slapd / backend.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /* backend.c - routines for dealing with back-end databases */
7
8
9 #include "portable.h"
10
11 #include <stdio.h>
12
13 #include <ac/string.h>
14 #include <ac/socket.h>
15
16 #include <sys/stat.h>
17
18 #include "slap.h"
19 #include "lutil.h"
20
21 #ifdef SLAPD_DNSSRV
22 #include "back-dnssrv/external.h"
23 #endif
24 #ifdef SLAPD_LDAP
25 #include "back-ldap/external.h"
26 #endif
27 #ifdef SLAPD_LDBM
28 #include "back-ldbm/external.h"
29 #endif
30 #ifdef SLAPD_PASSWD
31 #include "back-passwd/external.h"
32 #endif
33 #ifdef SLAPD_PERL
34 #include "back-perl/external.h"
35 #endif
36 #ifdef SLAPD_SHELL
37 #include "back-shell/external.h"
38 #endif
39 #ifdef SLAPD_TCL
40 #include "back-tcl/external.h"
41 #endif
42 #ifdef SLAPD_SQL
43 #include "back-sql/external.h"
44 #endif
45 #ifdef SLAPD_PRIVATE
46 #include "private/external.h"
47 #endif
48
49 static BackendInfo binfo[] = {
50 #if defined(SLAPD_DNSSRV) && !defined(SLAPD_DNSSRV_DYNAMIC)
51         {"dnssrv",      dnssrv_back_initialize},
52 #endif
53 #if defined(SLAPD_LDAP) && !defined(SLAPD_LDAP_DYNAMIC)
54         {"ldap",        ldap_back_initialize},
55 #endif
56 #if defined(SLAPD_LDBM) && !defined(SLAPD_LDBM_DYNAMIC)
57         {"ldbm",        ldbm_back_initialize},
58 #endif
59 #if defined(SLAPD_PASSWD) && !defined(SLAPD_PASSWD_DYNAMIC)
60         {"passwd",      passwd_back_initialize},
61 #endif
62 #if defined(SLAPD_PERL) && !defined(SLAPD_PERL_DYNAMIC)
63         {"perl",        perl_back_initialize},
64 #endif
65 #if defined(SLAPD_SHELL) && !defined(SLAPD_SHELL_DYNAMIC)
66         {"shell",       shell_back_initialize},
67 #endif
68 #if defined(SLAPD_TCL) && !defined(SLAPD_TCL_DYNAMIC)
69         {"tcl",         tcl_back_initialize},
70 #endif
71 #if defined(SLAPD_SQL) && !defined(SLAPD_SQL_DYNAMIC)
72         {"sql",         sql_back_initialize},
73 #endif
74         /* for any private backend */
75 #if defined(SLAPD_PRIVATE) && !defined(SLAPD_PRIVATE_DYNAMIC)
76         {"private",     private_back_initialize},
77 #endif
78         {NULL}
79 };
80
81 int                     nBackendInfo = 0;
82 BackendInfo     *backendInfo = NULL;
83
84 int                     nBackendDB = 0; 
85 BackendDB       *backendDB = NULL;
86
87 int backend_init(void)
88 {
89         int rc = -1;
90
91         if((nBackendInfo != 0) || (backendInfo != NULL)) {
92                 /* already initialized */
93                 Debug( LDAP_DEBUG_ANY,
94                         "backend_init: already initialized.\n", 0, 0, 0 );
95                 return -1;
96         }
97
98         for( ;
99                 binfo[nBackendInfo].bi_type != NULL;
100                 nBackendInfo++ )
101         {
102                 rc = binfo[nBackendInfo].bi_init( &binfo[nBackendInfo] );
103
104                 if(rc != 0) {
105                         Debug( LDAP_DEBUG_ANY,
106                                 "backend_init: initialized for type \"%s\"\n",
107                                         binfo[nBackendInfo].bi_type, 0, 0 );
108
109                         /* destroy those we've already inited */
110                         for( nBackendInfo--;
111                                 nBackendInfo >= 0 ;
112                                 nBackendInfo-- )
113                         { 
114                                 if ( binfo[nBackendInfo].bi_destroy ) {
115                                         binfo[nBackendInfo].bi_destroy(
116                                                 &binfo[nBackendInfo] );
117                                 }
118                         }
119                         return rc;
120                 }
121         }
122
123         if ( nBackendInfo > 0) {
124                 backendInfo = binfo;
125                 return 0;
126         }
127
128 #ifdef SLAPD_MODULES    
129         return 0;
130 #else
131         Debug( LDAP_DEBUG_ANY,
132                 "backend_init: failed\n",
133                 0, 0, 0 );
134
135         return rc;
136 #endif /* SLAPD_MODULES */
137 }
138
139 int backend_add(BackendInfo *aBackendInfo)
140 {
141    int rc = 0;
142
143    if ((rc = aBackendInfo->bi_init(aBackendInfo)) != 0) {
144       Debug( LDAP_DEBUG_ANY,
145              "backend_add: initialization for type \"%s\" failed\n",
146              aBackendInfo->bi_type, 0, 0 );
147       return rc;
148    }
149
150    /* now add the backend type to the Backend Info List */
151    {
152       BackendInfo *newBackendInfo = 0;
153
154       /* if backendInfo == binfo no deallocation of old backendInfo */
155       if (backendInfo == binfo) {
156          newBackendInfo = ch_calloc(nBackendInfo + 1, sizeof(BackendInfo));
157          AC_MEMCPY(newBackendInfo, backendInfo, sizeof(BackendInfo) * 
158                 nBackendInfo);
159       } else {
160          newBackendInfo = ch_realloc(backendInfo, sizeof(BackendInfo) * 
161                                      (nBackendInfo + 1));
162       }
163       AC_MEMCPY(&newBackendInfo[nBackendInfo], aBackendInfo, 
164              sizeof(BackendInfo));
165       backendInfo = newBackendInfo;
166       nBackendInfo++;
167
168       return 0;
169    }        
170 }
171
172 int backend_startup(Backend *be)
173 {
174         int i;
175         int rc = 0;
176
177         if( ! ( nBackendDB > 0 ) ) {
178                 /* no databases */
179                 Debug( LDAP_DEBUG_ANY,
180                         "backend_startup: %d databases to startup.\n",
181                         nBackendDB, 0, 0 );
182                 return 1;
183         }
184
185         if(be != NULL) {
186                 /* startup a specific backend database */
187                 Debug( LDAP_DEBUG_TRACE,
188                         "backend_startup: starting database\n",
189                         0, 0, 0 );
190
191                 if ( be->bd_info->bi_open ) {
192                         rc = be->bd_info->bi_open( be->bd_info );
193                 }
194
195                 if(rc != 0) {
196                         Debug( LDAP_DEBUG_ANY,
197                                 "backend_startup: bi_open failed!\n",
198                                 0, 0, 0 );
199                         return rc;
200                 }
201
202                 if ( be->bd_info->bi_db_open ) {
203                         rc = be->bd_info->bi_db_open( be );
204                 }
205
206                 if(rc != 0) {
207                         Debug( LDAP_DEBUG_ANY,
208                                 "backend_startup: bi_db_open failed!\n",
209                                 0, 0, 0 );
210                         return rc;
211                 }
212
213                 return rc;
214         }
215
216         /* open each backend type */
217         for( i = 0; i < nBackendInfo; i++ ) {
218                 if( backendInfo[i].bi_nDB == 0) {
219                         /* no database of this type, don't open */
220                         continue;
221                 }
222
223                 if( backendInfo[i].bi_open ) {
224                         rc = backendInfo[i].bi_open(
225                                 &backendInfo[i] );
226                 }
227
228                 if(rc != 0) {
229                         Debug( LDAP_DEBUG_ANY,
230                                 "backend_startup: bi_open %d failed!\n",
231                                 i, 0, 0 );
232                         return rc;
233                 }
234         }
235
236         /* open each backend database */
237         for( i = 0; i < nBackendDB; i++ ) {
238                 /* append global access controls */
239                 acl_append( &backendDB[i].be_acl, global_acl );
240
241                 if ( backendDB[i].bd_info->bi_db_open ) {
242                         rc = backendDB[i].bd_info->bi_db_open(
243                                 &backendDB[i] );
244                 }
245
246                 if(rc != 0) {
247                         Debug( LDAP_DEBUG_ANY,
248                                 "backend_startup: bi_db_open %d failed!\n",
249                                 i, 0, 0 );
250                         return rc;
251                 }
252         }
253
254         return rc;
255 }
256
257 int backend_num( Backend *be )
258 {
259         int i;
260
261         if( be == NULL ) return -1;
262
263         for( i = 0; i < nBackendDB; i++ ) {
264                 if( be == &backendDB[i] ) return i;
265         }
266         return -1;
267 }
268
269 int backend_shutdown( Backend *be )
270 {
271         int i;
272         int rc = 0;
273
274         if( be != NULL ) {
275                 /* shutdown a specific backend database */
276
277                 if ( be->bd_info->bi_nDB == 0 ) {
278                         /* no database of this type, we never opened it */
279                         return 0;
280                 }
281
282                 if ( be->bd_info->bi_db_close ) {
283                         be->bd_info->bi_db_close( be );
284                 }
285
286                 if( be->bd_info->bi_close ) {
287                         be->bd_info->bi_close( be->bd_info );
288                 }
289
290                 return 0;
291         }
292
293         /* close each backend database */
294         for( i = 0; i < nBackendDB; i++ ) {
295                 if ( backendDB[i].bd_info->bi_db_close ) {
296                         backendDB[i].bd_info->bi_db_close(
297                                 &backendDB[i] );
298                 }
299
300                 if(rc != 0) {
301                         Debug( LDAP_DEBUG_ANY,
302                                 "backend_close: bi_close %s failed!\n",
303                                 backendDB[i].be_type, 0, 0 );
304                 }
305         }
306
307         /* close each backend type */
308         for( i = 0; i < nBackendInfo; i++ ) {
309                 if( backendInfo[i].bi_nDB == 0 ) {
310                         /* no database of this type */
311                         continue;
312                 }
313
314                 if( backendInfo[i].bi_close ) {
315                         backendInfo[i].bi_close(
316                                 &backendInfo[i] );
317                 }
318         }
319
320         return 0;
321 }
322
323 int backend_destroy(void)
324 {
325         int i;
326
327         /* destroy each backend database */
328         for( i = 0; i < nBackendDB; i++ ) {
329                 if ( backendDB[i].bd_info->bi_db_destroy ) {
330                         backendDB[i].bd_info->bi_db_destroy(
331                                 &backendDB[i] );
332                 }
333         }
334
335         /* destroy each backend type */
336         for( i = 0; i < nBackendInfo; i++ ) {
337                 if( backendInfo[i].bi_destroy ) {
338                         backendInfo[i].bi_destroy(
339                                 &backendInfo[i] );
340                 }
341         }
342
343 #ifdef SLAPD_MODULES
344         if (backendInfo != binfo) {
345            free(backendInfo);
346         }
347 #endif /* SLAPD_MODULES */
348
349         nBackendInfo = 0;
350         backendInfo = NULL;
351
352         return 0;
353 }
354
355 BackendInfo* backend_info(const char *type)
356 {
357         int i;
358
359         /* search for the backend type */
360         for( i = 0; i < nBackendInfo; i++ ) {
361                 if( strcasecmp(backendInfo[i].bi_type, type) == 0 ) {
362                         return &backendInfo[i];
363                 }
364         }
365
366         return NULL;
367 }
368
369
370 BackendDB *
371 backend_db_init(
372     const char  *type
373 )
374 {
375         Backend *be;
376         BackendInfo *bi = backend_info(type);
377         int     rc = 0;
378
379         if( bi == NULL ) {
380                 fprintf( stderr, "Unrecognized database type (%s)\n", type );
381                 return NULL;
382         }
383
384         backendDB = (BackendDB *) ch_realloc(
385                         (char *) backendDB,
386                     (nBackendDB + 1) * sizeof(Backend) );
387
388         memset( &backendDB[nbackends], '\0', sizeof(Backend) );
389
390         be = &backends[nbackends++];
391
392         be->bd_info = bi;
393         be->be_sizelimit = defsize;
394         be->be_timelimit = deftime;
395         be->be_dfltaccess = global_default_access;
396
397         be->be_restrictops = global_restrictops;
398         be->be_requires = global_requires;
399
400         /* assign a default depth limit for alias deref */
401         be->be_max_deref_depth = SLAPD_DEFAULT_MAXDEREFDEPTH; 
402
403         if(bi->bi_db_init) {
404                 rc = bi->bi_db_init( be );
405         }
406
407         if(rc != 0) {
408                 fprintf( stderr, "database init failed (%s)\n", type );
409                 nbackends--;
410                 return NULL;
411         }
412
413         bi->bi_nDB++;
414         return( be );
415 }
416
417 void
418 be_db_close( void )
419 {
420         int     i;
421
422         for ( i = 0; i < nbackends; i++ ) {
423                 if ( backends[i].bd_info->bi_db_close ) {
424                         (*backends[i].bd_info->bi_db_close)( &backends[i] );
425                 }
426         }
427 }
428
429 Backend *
430 select_backend( const char * dn )
431 {
432         int     i, j, len, dnlen;
433
434         dnlen = strlen( dn );
435         for ( i = 0; i < nbackends; i++ ) {
436                 for ( j = 0; backends[i].be_nsuffix != NULL &&
437                     backends[i].be_nsuffix[j] != NULL; j++ )
438                 {
439                         len = strlen( backends[i].be_nsuffix[j] );
440
441                         if ( len > dnlen ) {
442                                 continue;
443                         }
444
445                         if ( strcmp( backends[i].be_nsuffix[j],
446                             dn + (dnlen - len) ) == 0 ) {
447                                 return( &backends[i] );
448                         }
449                 }
450         }
451
452         return( NULL );
453 }
454
455 int
456 be_issuffix(
457     Backend     *be,
458     const char  *suffix
459 )
460 {
461         int     i;
462
463         for ( i = 0; be->be_nsuffix != NULL && be->be_nsuffix[i] != NULL; i++ ) {
464                 if ( strcmp( be->be_nsuffix[i], suffix ) == 0 ) {
465                         return( 1 );
466                 }
467         }
468
469         return( 0 );
470 }
471
472 int
473 be_isroot( Backend *be, const char *ndn )
474 {
475         int rc;
476
477         if ( ndn == NULL || *ndn == '\0' ) {
478                 return( 0 );
479         }
480
481         if ( be->be_root_ndn == NULL || *be->be_root_ndn == '\0' ) {
482                 return( 0 );
483         }
484
485         rc = strcmp( be->be_root_ndn, ndn ) ? 0 : 1;
486
487         return(rc);
488 }
489
490 char *
491 be_root_dn( Backend *be )
492 {
493         if ( be->be_root_dn == NULL ) {
494                 return( "" );
495         }
496
497         return be->be_root_dn;
498 }
499
500 int
501 be_isroot_pw( Backend *be, const char *ndn, struct berval *cred )
502 {
503         int result;
504
505         if ( ! be_isroot( be, ndn ) ) {
506                 return 0;
507         }
508
509         if( be->be_root_pw.bv_len == 0 ) {
510                 return 0;
511         }
512
513 #ifdef SLAPD_CRYPT
514         ldap_pvt_thread_mutex_lock( &crypt_mutex );
515 #endif
516
517         result = lutil_passwd( &be->be_root_pw, cred, NULL );
518
519 #ifdef SLAPD_CRYPT
520         ldap_pvt_thread_mutex_unlock( &crypt_mutex );
521 #endif
522
523         return result == 0;
524 }
525
526 int
527 be_entry_release_rw( Backend *be, Entry *e, int rw )
528 {
529         if ( be->be_release ) {
530                 /* free and release entry from backend */
531                 return be->be_release( be, e, rw );
532         } else {
533                 /* free entry */
534                 entry_free( e );
535                 return 0;
536         }
537 }
538
539 int
540 backend_unbind(
541         Connection   *conn,
542         Operation    *op
543 )
544 {
545         int     i;
546
547         for ( i = 0; i < nbackends; i++ ) {
548                 if ( backends[i].be_unbind ) {
549                         (*backends[i].be_unbind)( &backends[i], conn, op );
550                 }
551         }
552
553         return 0;
554 }
555
556 int
557 backend_connection_init(
558         Connection   *conn
559 )
560 {
561         int     i;
562
563         for ( i = 0; i < nbackends; i++ ) {
564                 if ( backends[i].be_connection_init ) {
565                         (*backends[i].be_connection_init)( &backends[i], conn);
566                 }
567         }
568
569         return 0;
570 }
571
572 int
573 backend_connection_destroy(
574         Connection   *conn
575 )
576 {
577         int     i;
578
579         for ( i = 0; i < nbackends; i++ ) {
580                 if ( backends[i].be_connection_destroy ) {
581                         (*backends[i].be_connection_destroy)( &backends[i], conn);
582                 }
583         }
584
585         return 0;
586 }
587
588 static int
589 backend_check_controls(
590         Backend *be,
591         Connection *conn,
592         Operation *op,
593         const char **text )
594 {
595         LDAPControl **ctrls;
596         ctrls = op->o_ctrls;
597         if( ctrls == NULL ) {
598                 return LDAP_SUCCESS;
599         }
600
601         for( ; *ctrls != NULL ; ctrls++ ) {
602                 if( (*ctrls)->ldctl_iscritical &&
603                         !charray_inlist( be->be_controls, (*ctrls)->ldctl_oid ) )
604                 {
605                         *text = "control unavailable in NamingContext";
606                         return LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
607                 }
608         }
609
610         return LDAP_SUCCESS;
611 }
612
613 int
614 backend_check_restrictions(
615         Backend *be,
616         Connection *conn,
617         Operation *op,
618         const char *extoid,
619         const char **text )
620 {
621         int rc;
622         slap_mask_t restrictops;
623         slap_mask_t requires;
624         slap_mask_t opflag;
625         slap_ssf_set_t *ssf;
626         int updateop = 0;
627
628         if( be ) {
629                 rc = backend_check_controls( be, conn, op, text );
630
631                 if( rc != LDAP_SUCCESS ) {
632                         return rc;
633                 }
634
635                 restrictops = be->be_restrictops;
636                 requires = be->be_requires;
637                 ssf = &be->be_ssf_set;
638
639         } else {
640                 restrictops = global_restrictops;
641                 requires = global_requires;
642                 ssf = &global_ssf_set;
643         }
644
645         switch( op->o_tag ) {
646         case LDAP_REQ_ADD:
647                 opflag = SLAP_RESTRICT_OP_ADD;
648                 updateop++;
649                 break;
650         case LDAP_REQ_BIND:
651                 opflag = SLAP_RESTRICT_OP_BIND;
652                 break;
653         case LDAP_REQ_COMPARE:
654                 opflag = SLAP_RESTRICT_OP_COMPARE;
655                 break;
656         case LDAP_REQ_DELETE:
657                 updateop++;
658                 opflag = SLAP_RESTRICT_OP_DELETE;
659                 break;
660         case LDAP_REQ_EXTENDED:
661                 opflag = SLAP_RESTRICT_OP_EXTENDED;
662                 break;
663         case LDAP_REQ_MODIFY:
664                 updateop++;
665                 opflag = SLAP_RESTRICT_OP_MODIFY;
666                 break;
667         case LDAP_REQ_RENAME:
668                 updateop++;
669                 opflag = SLAP_RESTRICT_OP_RENAME;
670                 break;
671         case LDAP_REQ_SEARCH:
672                 opflag = SLAP_RESTRICT_OP_SEARCH;
673                 break;
674         case LDAP_REQ_UNBIND:
675                 opflag = 0;
676                 break;
677         default:
678                 *text = "restrict operations internal error";
679                 return LDAP_OTHER;
680         }
681
682         if (( extoid == NULL || strcmp( extoid, LDAP_EXOP_START_TLS ) ) ) {
683                 /* these checks don't apply to StartTLS */
684
685                 if( op->o_tag == LDAP_REQ_EXTENDED ) {
686                         /* threat other extended operations as update ops */
687                         updateop++;
688                 }
689
690                 if( op->o_ssf < ssf->sss_ssf ) {
691                         *text = "confidentiality required";
692                         return LDAP_CONFIDENTIALITY_REQUIRED;
693                 }
694                 if( op->o_transport_ssf < ssf->sss_transport ) {
695                         *text = "transport confidentiality required";
696                         return LDAP_CONFIDENTIALITY_REQUIRED;
697                 }
698                 if( op->o_tls_ssf < ssf->sss_tls ) {
699                         *text = "TLS confidentiality required";
700                         return LDAP_CONFIDENTIALITY_REQUIRED;
701                 }
702                 if( op->o_sasl_ssf < ssf->sss_sasl ) {
703                         *text = "SASL confidentiality required";
704                         return LDAP_CONFIDENTIALITY_REQUIRED;
705                 }
706
707                 if( updateop ) {
708                         if( op->o_ssf < ssf->sss_update_ssf ) {
709                                 *text = "update confidentiality required";
710                                 return LDAP_CONFIDENTIALITY_REQUIRED;
711                         }
712                         if( op->o_transport_ssf < ssf->sss_update_transport ) {
713                                 *text = "transport update confidentiality required";
714                                 return LDAP_CONFIDENTIALITY_REQUIRED;
715                         }
716                         if( op->o_tls_ssf < ssf->sss_update_tls ) {
717                                 *text = "TLS update confidentiality required";
718                                 return LDAP_CONFIDENTIALITY_REQUIRED;
719                         }
720                         if( op->o_sasl_ssf < ssf->sss_update_sasl ) {
721                                 *text = "SASL update confidentiality required";
722                                 return LDAP_CONFIDENTIALITY_REQUIRED;
723                         }
724                 }
725         }
726
727         if (( extoid == NULL || strcmp( extoid, LDAP_EXOP_START_TLS ) )
728                 || op->o_tag == LDAP_REQ_BIND )
729         {
730                 /* these checks don't apply to StartTLS or Bind */
731
732                 if( requires & SLAP_REQUIRE_STRONG ) {
733                         /* should check mechanism */
734                         if( op->o_authmech == NULL ||
735                                 op->o_dn == NULL || *op->o_dn == '\0' )
736                         {
737                                 *text = "strong authentication required";
738                                 return LDAP_STRONG_AUTH_REQUIRED;
739                         }
740                 }
741
742                 if( requires & SLAP_REQUIRE_SASL ) {
743                         if( op->o_authmech == NULL ||
744                                 op->o_dn == NULL || *op->o_dn == '\0' )
745                         {
746                                 *text = "SASL authentication required";
747                                 return LDAP_STRONG_AUTH_REQUIRED;
748                         }
749                 }
750                         
751                 if( requires & SLAP_REQUIRE_AUTHC ) {
752                         if( op->o_dn == NULL || *op->o_dn == '\0' ) {
753                                 *text = "authentication required";
754                                 return LDAP_UNWILLING_TO_PERFORM;
755                         }
756                 }
757
758                 if( requires & SLAP_REQUIRE_BIND ) {
759                         int version;
760                         ldap_pvt_thread_mutex_lock( &conn->c_mutex );
761                         version = conn->c_protocol;
762                         ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
763
764                         if( !version ) {
765                                 /* no bind has occurred */
766                                 *text = "BIND required";
767                                 return LDAP_OPERATIONS_ERROR;
768                         }
769                 }
770
771                 if( requires & SLAP_REQUIRE_LDAP_V3 ) {
772                         if( op->o_protocol < LDAP_VERSION3 ) {
773                                 /* no bind has occurred */
774                                 *text = "operation restricted to LDAPv3 clients";
775                                 return LDAP_OPERATIONS_ERROR;
776                         }
777                 }
778         }
779
780         if( restrictops & opflag ) {
781                 if( restrictops == SLAP_RESTRICT_OP_READS ) {
782                         *text = "read operations restricted";
783                 } else {
784                         *text = "operation restricted";
785                 }
786                 return LDAP_UNWILLING_TO_PERFORM;
787         }
788
789         return LDAP_SUCCESS;
790 }
791
792 int backend_check_referrals(
793         Backend *be,
794         Connection *conn,
795         Operation *op,
796         const char *dn,
797         const char *ndn )
798 {
799         int rc = LDAP_SUCCESS;
800
801         if( be->be_chk_referrals ) {
802                 const char *text;
803
804                 rc = be->be_chk_referrals( be,
805                         conn, op, dn, ndn, &text );
806
807                 if( rc != LDAP_SUCCESS && rc != LDAP_REFERRAL ) {
808                         send_ldap_result( conn, op, rc,
809                                 NULL, text, NULL, NULL );
810                 }
811         }
812
813         return rc;
814 }
815
816 int 
817 backend_group(
818         Backend *be,
819         Entry   *target,
820         const char      *gr_ndn,
821         const char      *op_ndn,
822         ObjectClass *group_oc,
823         AttributeDescription *group_at
824 )
825 {
826         if( strcmp( target->e_ndn, gr_ndn ) != 0 ) {
827                 /* we won't attempt to send it to a different backend */
828                 
829                 be = select_backend(gr_ndn);
830
831                 if (be == NULL) {
832                         return LDAP_NO_SUCH_OBJECT;
833                 }
834         } 
835
836         if( be->be_group ) {
837                 return be->be_group( be, target, gr_ndn, op_ndn,
838                         group_oc, group_at );
839         }
840
841         return LDAP_UNWILLING_TO_PERFORM;
842 }
843
844 int 
845 backend_attribute(
846         Backend *be,
847         Connection *conn,
848         Operation *op,
849         Entry   *target,
850         const char      *e_ndn,
851         AttributeDescription *entry_at,
852         struct berval ***vals
853 )
854 {
855         if( target == NULL || strcmp( target->e_ndn, e_ndn ) != 0 ) {
856                 /* we won't attempt to send it to a different backend */
857                 
858                 be = select_backend(e_ndn);
859
860                 if (be == NULL) {
861                         return LDAP_NO_SUCH_OBJECT;
862                 }
863         } 
864
865         if( be->be_attribute ) {
866                 return be->be_attribute( be, conn, op, target, e_ndn,
867                         entry_at, vals );
868         }
869
870         return LDAP_UNWILLING_TO_PERFORM;
871 }
872
873 Attribute *backend_operational(
874         Backend *be,
875         Entry *e )
876 {
877         Attribute *a = NULL;
878
879 #ifdef SLAPD_SCHEMA_DN
880         a = ch_malloc( sizeof( Attribute ) );
881         a->a_desc = ad_dup( slap_schema.si_ad_subschemaSubentry );
882
883         /* Should be backend specific */
884         a->a_vals = ch_malloc( 2 * sizeof( struct berval * ) );
885         a->a_vals[0] = ber_bvstrdup( SLAPD_SCHEMA_DN );
886         a->a_vals[1] = NULL;
887
888         a->a_next = NULL;
889 #endif
890
891         return a;
892 }