]> git.sur5r.net Git - openldap/blob - servers/slapd/backend.c
ca38d9f330b480bb3d487eb50bc0f3eddb15fd47
[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         be->be_realm = global_realm != NULL
404                 ? ch_strdup( global_realm ) : NULL;
405
406         if(bi->bi_db_init) {
407                 rc = bi->bi_db_init( be );
408         }
409
410         if(rc != 0) {
411                 fprintf( stderr, "database init failed (%s)\n", type );
412                 nbackends--;
413                 return NULL;
414         }
415
416         bi->bi_nDB++;
417         return( be );
418 }
419
420 void
421 be_db_close( void )
422 {
423         int     i;
424
425         for ( i = 0; i < nbackends; i++ ) {
426                 if ( backends[i].bd_info->bi_db_close ) {
427                         (*backends[i].bd_info->bi_db_close)( &backends[i] );
428                 }
429         }
430 }
431
432 Backend *
433 select_backend( const char * dn )
434 {
435         int     i, j, len, dnlen;
436
437         dnlen = strlen( dn );
438         for ( i = 0; i < nbackends; i++ ) {
439                 for ( j = 0; backends[i].be_nsuffix != NULL &&
440                     backends[i].be_nsuffix[j] != NULL; j++ )
441                 {
442                         len = strlen( backends[i].be_nsuffix[j] );
443
444                         if ( len > dnlen ) {
445                                 continue;
446                         }
447
448                         if ( strcmp( backends[i].be_nsuffix[j],
449                             dn + (dnlen - len) ) == 0 ) {
450                                 return( &backends[i] );
451                         }
452                 }
453         }
454
455         return( NULL );
456 }
457
458 int
459 be_issuffix(
460     Backend     *be,
461     const char  *suffix
462 )
463 {
464         int     i;
465
466         for ( i = 0; be->be_nsuffix != NULL && be->be_nsuffix[i] != NULL; i++ ) {
467                 if ( strcmp( be->be_nsuffix[i], suffix ) == 0 ) {
468                         return( 1 );
469                 }
470         }
471
472         return( 0 );
473 }
474
475 int
476 be_isroot( Backend *be, const char *ndn )
477 {
478         int rc;
479
480         if ( ndn == NULL || *ndn == '\0' ) {
481                 return( 0 );
482         }
483
484         if ( be->be_root_ndn == NULL || *be->be_root_ndn == '\0' ) {
485                 return( 0 );
486         }
487
488         rc = strcmp( be->be_root_ndn, ndn ) ? 0 : 1;
489
490         return(rc);
491 }
492
493 char *
494 be_root_dn( Backend *be )
495 {
496         if ( be->be_root_dn == NULL ) {
497                 return( "" );
498         }
499
500         return be->be_root_dn;
501 }
502
503 int
504 be_isroot_pw( Backend *be, const char *ndn, struct berval *cred )
505 {
506         int result;
507
508         if ( ! be_isroot( be, ndn ) ) {
509                 return 0;
510         }
511
512         if( be->be_root_pw.bv_len == 0 ) {
513                 return 0;
514         }
515
516 #ifdef SLAPD_CRYPT
517         ldap_pvt_thread_mutex_lock( &crypt_mutex );
518 #endif
519
520         result = lutil_passwd( &be->be_root_pw, cred, NULL );
521
522 #ifdef SLAPD_CRYPT
523         ldap_pvt_thread_mutex_unlock( &crypt_mutex );
524 #endif
525
526         return result == 0;
527 }
528
529 int
530 be_entry_release_rw( Backend *be, Entry *e, int rw )
531 {
532         if ( be->be_release ) {
533                 /* free and release entry from backend */
534                 return be->be_release( be, e, rw );
535         } else {
536                 /* free entry */
537                 entry_free( e );
538                 return 0;
539         }
540 }
541
542 int
543 backend_unbind(
544         Connection   *conn,
545         Operation    *op
546 )
547 {
548         int     i;
549
550         for ( i = 0; i < nbackends; i++ ) {
551                 if ( backends[i].be_unbind ) {
552                         (*backends[i].be_unbind)( &backends[i], conn, op );
553                 }
554         }
555
556         return 0;
557 }
558
559 int
560 backend_connection_init(
561         Connection   *conn
562 )
563 {
564         int     i;
565
566         for ( i = 0; i < nbackends; i++ ) {
567                 if ( backends[i].be_connection_init ) {
568                         (*backends[i].be_connection_init)( &backends[i], conn);
569                 }
570         }
571
572         return 0;
573 }
574
575 int
576 backend_connection_destroy(
577         Connection   *conn
578 )
579 {
580         int     i;
581
582         for ( i = 0; i < nbackends; i++ ) {
583                 if ( backends[i].be_connection_destroy ) {
584                         (*backends[i].be_connection_destroy)( &backends[i], conn);
585                 }
586         }
587
588         return 0;
589 }
590
591 static int
592 backend_check_controls(
593         Backend *be,
594         Connection *conn,
595         Operation *op,
596         const char **text )
597 {
598         LDAPControl **ctrls;
599         ctrls = op->o_ctrls;
600         if( ctrls == NULL ) {
601                 return LDAP_SUCCESS;
602         }
603
604         for( ; *ctrls != NULL ; ctrls++ ) {
605                 if( (*ctrls)->ldctl_iscritical &&
606                         !charray_inlist( be->be_controls, (*ctrls)->ldctl_oid ) )
607                 {
608                         *text = "control unavailable in NamingContext";
609                         return LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
610                 }
611         }
612
613         return LDAP_SUCCESS;
614 }
615
616 int
617 backend_check_restrictions(
618         Backend *be,
619         Connection *conn,
620         Operation *op,
621         const char *extoid,
622         const char **text )
623 {
624         int rc;
625         slap_mask_t restrictops;
626         slap_mask_t requires;
627         slap_mask_t opflag;
628         slap_ssf_set_t *ssf;
629         int updateop = 0;
630
631         if( be ) {
632                 rc = backend_check_controls( be, conn, op, text );
633
634                 if( rc != LDAP_SUCCESS ) {
635                         return rc;
636                 }
637
638                 restrictops = be->be_restrictops;
639                 requires = be->be_requires;
640                 ssf = &be->be_ssf_set;
641
642         } else {
643                 restrictops = global_restrictops;
644                 requires = global_requires;
645                 ssf = &global_ssf_set;
646         }
647
648         switch( op->o_tag ) {
649         case LDAP_REQ_ADD:
650                 opflag = SLAP_RESTRICT_OP_ADD;
651                 updateop++;
652                 break;
653         case LDAP_REQ_BIND:
654                 opflag = SLAP_RESTRICT_OP_BIND;
655                 break;
656         case LDAP_REQ_COMPARE:
657                 opflag = SLAP_RESTRICT_OP_COMPARE;
658                 break;
659         case LDAP_REQ_DELETE:
660                 updateop++;
661                 opflag = SLAP_RESTRICT_OP_DELETE;
662                 break;
663         case LDAP_REQ_EXTENDED:
664                 opflag = SLAP_RESTRICT_OP_EXTENDED;
665                 break;
666         case LDAP_REQ_MODIFY:
667                 updateop++;
668                 opflag = SLAP_RESTRICT_OP_MODIFY;
669                 break;
670         case LDAP_REQ_RENAME:
671                 updateop++;
672                 opflag = SLAP_RESTRICT_OP_RENAME;
673                 break;
674         case LDAP_REQ_SEARCH:
675                 opflag = SLAP_RESTRICT_OP_SEARCH;
676                 break;
677         case LDAP_REQ_UNBIND:
678                 opflag = 0;
679                 break;
680         default:
681                 *text = "restrict operations internal error";
682                 return LDAP_OTHER;
683         }
684
685         if( ( extoid == NULL || strcmp( extoid, LDAP_EXOP_START_TLS ) )
686                 && op->o_tag != LDAP_REQ_BIND )
687         {
688                 /* these checks don't apply to bind nor StartTLS */
689
690                 if( op->o_tag == LDAP_REQ_EXTENDED ) {
691                         /* threat other extended operations as update ops */
692                         updateop++;
693                 }
694
695                 if( op->o_ssf < ssf->sss_ssf ) {
696                         *text = "confidentiality required";
697                         return LDAP_CONFIDENTIALITY_REQUIRED;
698                 }
699                 if( op->o_transport_ssf < ssf->sss_transport ) {
700                         *text = "transport confidentiality required";
701                         return LDAP_CONFIDENTIALITY_REQUIRED;
702                 }
703                 if( op->o_tls_ssf < ssf->sss_tls ) {
704                         *text = "TLS confidentiality required";
705                         return LDAP_CONFIDENTIALITY_REQUIRED;
706                 }
707                 if( op->o_sasl_ssf < ssf->sss_sasl ) {
708                         *text = "SASL confidentiality required";
709                         return LDAP_CONFIDENTIALITY_REQUIRED;
710                 }
711
712                 if( updateop ) {
713                         if( op->o_ssf < ssf->sss_update_ssf ) {
714                                 *text = "update confidentiality required";
715                                 return LDAP_CONFIDENTIALITY_REQUIRED;
716                         }
717                         if( op->o_transport_ssf < ssf->sss_update_transport ) {
718                                 *text = "transport update confidentiality required";
719                                 return LDAP_CONFIDENTIALITY_REQUIRED;
720                         }
721                         if( op->o_tls_ssf < ssf->sss_update_tls ) {
722                                 *text = "TLS update confidentiality required";
723                                 return LDAP_CONFIDENTIALITY_REQUIRED;
724                         }
725                         if( op->o_sasl_ssf < ssf->sss_update_sasl ) {
726                                 *text = "SASL update confidentiality required";
727                                 return LDAP_CONFIDENTIALITY_REQUIRED;
728                         }
729                 }
730
731                 if( requires & SLAP_REQUIRE_STRONG ) {
732                         /* should check mechanism */
733                         if( op->o_authmech == NULL ||
734                                 op->o_dn == NULL || *op->o_dn == '\0' )
735                         {
736                                 *text = "SASL authentication required";
737                                 return LDAP_STRONG_AUTH_REQUIRED;
738                         }
739                 }
740
741                 if( requires & SLAP_REQUIRE_SASL ) {
742                         if( op->o_authmech == NULL ||
743                                 op->o_dn == NULL || *op->o_dn == '\0' )
744                         {
745                                 *text = "SASL authentication required";
746                                 return LDAP_STRONG_AUTH_REQUIRED;
747                         }
748                 }
749                         
750                 if( requires & SLAP_REQUIRE_AUTHC ) {
751                         if( op->o_dn == NULL || *op->o_dn == '\0' ) {
752                                 *text = "authentication required";
753                                 return LDAP_UNWILLING_TO_PERFORM;
754                         }
755                 }
756
757                 if( requires & SLAP_REQUIRE_BIND ) {
758                         int version;
759                         ldap_pvt_thread_mutex_lock( &conn->c_mutex );
760                         version = conn->c_protocol;
761                         ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
762
763                         if( !version ) {
764                                 /* no bind has occurred */
765                                 *text = "BIND required";
766                                 return LDAP_OPERATIONS_ERROR;
767                         }
768                 }
769
770                 if( requires & SLAP_REQUIRE_LDAP_V3 ) {
771                         if( op->o_protocol < LDAP_VERSION3 ) {
772                                 /* no bind has occurred */
773                                 *text = "operation restricted to LDAPv3 clients";
774                                 return LDAP_OPERATIONS_ERROR;
775                         }
776                 }
777         }
778
779         if( restrictops & opflag ) {
780                 if( (restrictops & SLAP_RESTRICT_OP_READS)
781                         == SLAP_RESTRICT_OP_READS )
782                 {
783                         *text = "read operations restricted";
784                 } else {
785                         *text = "operation restricted";
786                 }
787                 return LDAP_UNWILLING_TO_PERFORM;
788         }
789
790         return LDAP_SUCCESS;
791 }
792
793 int backend_check_referrals(
794         Backend *be,
795         Connection *conn,
796         Operation *op,
797         const char *dn,
798         const char *ndn )
799 {
800         int rc = LDAP_SUCCESS;
801
802         if( be->be_chk_referrals ) {
803                 const char *text;
804
805                 rc = be->be_chk_referrals( be,
806                         conn, op, dn, ndn, &text );
807
808                 if( rc != LDAP_SUCCESS && rc != LDAP_REFERRAL ) {
809                         send_ldap_result( conn, op, rc,
810                                 NULL, text, NULL, NULL );
811                 }
812         }
813
814         return rc;
815 }
816
817 int 
818 backend_group(
819         Backend *be,
820         Entry   *target,
821         const char      *gr_ndn,
822         const char      *op_ndn,
823         ObjectClass *group_oc,
824         AttributeDescription *group_at
825 )
826 {
827         if( strcmp( target->e_ndn, gr_ndn ) != 0 ) {
828                 /* we won't attempt to send it to a different backend */
829                 
830                 be = select_backend(gr_ndn);
831
832                 if (be == NULL) {
833                         return LDAP_NO_SUCH_OBJECT;
834                 }
835         } 
836
837         if( be->be_group ) {
838                 return be->be_group( be, target, gr_ndn, op_ndn,
839                         group_oc, group_at );
840         }
841
842         return LDAP_UNWILLING_TO_PERFORM;
843 }
844
845 int 
846 backend_attribute(
847         Backend *be,
848         Connection *conn,
849         Operation *op,
850         Entry   *target,
851         const char      *e_ndn,
852         AttributeDescription *entry_at,
853         struct berval ***vals
854 )
855 {
856         if( target == NULL || strcmp( target->e_ndn, e_ndn ) != 0 ) {
857                 /* we won't attempt to send it to a different backend */
858                 
859                 be = select_backend(e_ndn);
860
861                 if (be == NULL) {
862                         return LDAP_NO_SUCH_OBJECT;
863                 }
864         } 
865
866         if( be->be_attribute ) {
867                 return be->be_attribute( be, conn, op, target, e_ndn,
868                         entry_at, vals );
869         }
870
871         return LDAP_UNWILLING_TO_PERFORM;
872 }
873
874 Attribute *backend_operational(
875         Backend *be,
876         Entry *e )
877 {
878         Attribute *a = NULL;
879
880 #ifdef SLAPD_SCHEMA_DN
881         a = ch_malloc( sizeof( Attribute ) );
882         a->a_desc = ad_dup( slap_schema.si_ad_subschemaSubentry );
883
884         /* Should be backend specific */
885         a->a_vals = ch_malloc( 2 * sizeof( struct berval * ) );
886         a->a_vals[0] = ber_bvstrdup( SLAPD_SCHEMA_DN );
887         a->a_vals[1] = NULL;
888
889         a->a_next = NULL;
890 #endif
891
892         return a;
893 }