2009-11-23 20:30:35 +11:00
/*
Samba4 module loading module
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2009
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
/*
* Name : ldb
*
* Component : Samba4 module loading module
*
* Description : Implement a single ' module ' in the ldb database ,
* which loads the remaining modules based on ' choice of configuration ' attributes
*
* This is to avoid forcing a reprovision of the ldb databases when we change the internal structure of the code
*
* Author : Andrew Bartlett
*/
# include "includes.h"
2011-02-10 14:12:51 +11:00
# include <ldb.h>
# include <ldb_errors.h>
# include <ldb_module.h>
2009-11-23 20:30:35 +11:00
# include "dsdb/samdb/ldb_modules/util.h"
# include "dsdb/samdb/samdb.h"
2009-12-18 13:47:46 +11:00
# include "librpc/ndr/libndr.h"
2013-09-10 11:38:10 +12:00
# include "auth/credentials/credentials.h"
# include "param/secrets.h"
# include "lib/ldb-samba/ldb_wrap.h"
2009-11-23 20:30:35 +11:00
static int read_at_rootdse_record ( struct ldb_context * ldb , struct ldb_module * module , TALLOC_CTX * mem_ctx ,
2011-01-17 13:12:15 +11:00
struct ldb_message * * msg , struct ldb_request * parent )
2009-11-23 20:30:35 +11:00
{
int ret ;
static const char * rootdse_attrs [ ] = { " defaultNamingContext " , " configurationNamingContext " , " schemaNamingContext " , NULL } ;
struct ldb_result * rootdse_res ;
struct ldb_dn * rootdse_dn ;
TALLOC_CTX * tmp_ctx = talloc_new ( mem_ctx ) ;
if ( ! tmp_ctx ) {
2010-07-06 13:21:54 +10:00
return ldb_oom ( ldb ) ;
2009-11-23 20:30:35 +11:00
}
rootdse_dn = ldb_dn_new ( tmp_ctx , ldb , " @ROOTDSE " ) ;
if ( ! rootdse_dn ) {
talloc_free ( tmp_ctx ) ;
2010-07-06 13:21:54 +10:00
return ldb_oom ( ldb ) ;
2009-11-23 20:30:35 +11:00
}
2010-07-06 03:23:21 +03:00
ret = dsdb_module_search_dn ( module , tmp_ctx , & rootdse_res , rootdse_dn ,
2011-01-17 13:12:15 +11:00
rootdse_attrs , DSDB_FLAG_NEXT_MODULE , parent ) ;
2009-11-23 20:30:35 +11:00
if ( ret ! = LDB_SUCCESS ) {
talloc_free ( tmp_ctx ) ;
return ret ;
}
talloc_steal ( mem_ctx , rootdse_res - > msgs ) ;
* msg = rootdse_res - > msgs [ 0 ] ;
talloc_free ( tmp_ctx ) ;
return ret ;
}
static int prepare_modules_line ( struct ldb_context * ldb ,
TALLOC_CTX * mem_ctx ,
const struct ldb_message * rootdse_msg ,
struct ldb_message * msg , const char * backend_attr ,
const char * backend_mod , const char * * backend_mod_list )
{
int ret ;
const char * * backend_full_list ;
const char * backend_dn ;
char * mod_list_string ;
char * full_string ;
TALLOC_CTX * tmp_ctx = talloc_new ( mem_ctx ) ;
if ( ! tmp_ctx ) {
2010-07-06 13:21:54 +10:00
return ldb_oom ( ldb ) ;
2009-11-23 20:30:35 +11:00
}
if ( backend_attr ) {
backend_dn = ldb_msg_find_attr_as_string ( rootdse_msg , backend_attr , NULL ) ;
if ( ! backend_dn ) {
ldb_asprintf_errstring ( ldb ,
" samba_dsdb_init: "
" unable to read %s from %s:%s " ,
backend_attr , ldb_dn_get_linearized ( rootdse_msg - > dn ) ,
ldb_errstring ( ldb ) ) ;
return LDB_ERR_CONSTRAINT_VIOLATION ;
}
} else {
backend_dn = " * " ;
}
if ( backend_mod ) {
2014-02-27 09:29:36 +01:00
char * * b = str_list_make_single ( tmp_ctx , backend_mod ) ;
backend_full_list = discard_const_p ( const char * , b ) ;
2009-11-23 20:30:35 +11:00
} else {
2014-02-27 09:29:36 +01:00
char * * b = str_list_make_empty ( tmp_ctx ) ;
backend_full_list = discard_const_p ( const char * , b ) ;
2009-11-23 20:30:35 +11:00
}
if ( ! backend_full_list ) {
talloc_free ( tmp_ctx ) ;
2010-07-06 13:21:54 +10:00
return ldb_oom ( ldb ) ;
2009-11-23 20:30:35 +11:00
}
backend_full_list = str_list_append_const ( backend_full_list , backend_mod_list ) ;
if ( ! backend_full_list ) {
talloc_free ( tmp_ctx ) ;
2010-07-06 13:21:54 +10:00
return ldb_oom ( ldb ) ;
2009-11-23 20:30:35 +11:00
}
mod_list_string = str_list_join ( tmp_ctx , backend_full_list , ' , ' ) ;
if ( ! mod_list_string ) {
talloc_free ( tmp_ctx ) ;
2010-07-06 13:21:54 +10:00
return ldb_oom ( ldb ) ;
2009-11-23 20:30:35 +11:00
}
full_string = talloc_asprintf ( tmp_ctx , " %s:%s " , backend_dn , mod_list_string ) ;
ret = ldb_msg_add_steal_string ( msg , " modules " , full_string ) ;
talloc_free ( tmp_ctx ) ;
return ret ;
}
2013-09-10 11:38:10 +12:00
/*
* Force overwrite of the credentials with those
* specified in secrets . ldb , to connect across the
* ldapi socket to an LDAP backend
*/
2009-12-18 13:47:46 +11:00
2013-09-17 14:04:06 -07:00
static int set_ldap_credentials ( struct ldb_context * ldb , bool use_external )
2013-09-10 11:38:10 +12:00
{
const char * secrets_ldb_path , * sam_ldb_path ;
char * private_dir , * p , * error_string ;
struct ldb_context * secrets_ldb ;
struct cli_credentials * cred ;
struct loadparm_context * lp_ctx = ldb_get_opaque ( ldb , " loadparm " ) ;
TALLOC_CTX * tmp_ctx = talloc_new ( ldb ) ;
if ( ! tmp_ctx ) {
return ldb_oom ( ldb ) ;
}
cred = cli_credentials_init ( ldb ) ;
if ( ! cred ) {
talloc_free ( tmp_ctx ) ;
return ldb_oom ( ldb ) ;
}
cli_credentials_set_anonymous ( cred ) ;
2013-09-17 14:04:06 -07:00
if ( use_external ) {
cli_credentials_set_forced_sasl_mech ( cred , " EXTERNAL " ) ;
2013-09-10 11:38:10 +12:00
} else {
2013-09-17 14:04:06 -07:00
cli_credentials_set_forced_sasl_mech ( cred , " DIGEST-MD5 " ) ;
/*
* We don ' t want to use krb5 to talk to our samdb - recursion
* here would be bad , and this account isn ' t in the KDC
* anyway
*/
cli_credentials_set_kerberos_state ( cred , CRED_DONT_USE_KERBEROS ) ;
/*
* Work out where * our * secrets . ldb is . It must be in
* the same directory as sam . ldb
*/
sam_ldb_path = ( const char * ) ldb_get_opaque ( ldb , " ldb_url " ) ;
if ( ! sam_ldb_path ) {
talloc_free ( tmp_ctx ) ;
return ldb_operr ( ldb ) ;
}
if ( strncmp ( " tdb:// " , sam_ldb_path , 6 ) = = 0 ) {
sam_ldb_path + = 6 ;
}
private_dir = talloc_strdup ( tmp_ctx , sam_ldb_path ) ;
p = strrchr ( private_dir , ' / ' ) ;
if ( p ) {
* p = ' \0 ' ;
} else {
private_dir = talloc_strdup ( tmp_ctx , " . " ) ;
}
2013-09-10 11:38:10 +12:00
2013-09-17 14:04:06 -07:00
secrets_ldb_path = talloc_asprintf ( private_dir , " tdb://%s/secrets.ldb " ,
private_dir ) ;
2013-09-10 11:38:10 +12:00
2013-09-17 14:04:06 -07:00
if ( ! secrets_ldb_path ) {
talloc_free ( tmp_ctx ) ;
return ldb_oom ( ldb ) ;
}
2013-09-10 11:38:10 +12:00
2013-09-17 14:04:06 -07:00
/*
* Now that we have found the location , connect to
* secrets . ldb so we can read the SamDB Credentials
* record
*/
secrets_ldb = ldb_wrap_connect ( tmp_ctx , NULL , lp_ctx , secrets_ldb_path ,
NULL , NULL , 0 ) ;
if ( ! NT_STATUS_IS_OK ( cli_credentials_set_secrets ( cred , NULL , secrets_ldb , NULL ,
SECRETS_LDAP_FILTER , & error_string ) ) ) {
ldb_asprintf_errstring ( ldb , " Failed to read LDAP backend password from %s " , secrets_ldb_path ) ;
talloc_free ( tmp_ctx ) ;
return LDB_ERR_STRONG_AUTH_REQUIRED ;
}
2013-09-10 11:38:10 +12:00
}
/*
* Finally overwrite any supplied credentials with
* these ones , as only secrets . ldb contains the magic
* credentials to talk on the ldapi socket
*/
if ( ldb_set_opaque ( ldb , " credentials " , cred ) ) {
talloc_free ( tmp_ctx ) ;
return ldb_operr ( ldb ) ;
}
talloc_free ( tmp_ctx ) ;
return LDB_SUCCESS ;
}
2009-12-18 13:47:46 +11:00
2009-11-23 20:30:35 +11:00
static int samba_dsdb_init ( struct ldb_module * module )
{
struct ldb_context * ldb = ldb_module_get_ctx ( module ) ;
int ret , len , i ;
TALLOC_CTX * tmp_ctx = talloc_new ( module ) ;
struct ldb_result * res ;
2013-12-10 17:52:29 +01:00
struct ldb_message * rootdse_msg = NULL , * partition_msg ;
2013-09-17 14:04:06 -07:00
struct ldb_dn * samba_dsdb_dn , * partition_dn ;
2009-11-23 20:30:35 +11:00
struct ldb_module * backend_module , * module_chain ;
const char * * final_module_list , * * reverse_module_list ;
/*
Add modules to the list to activate them by default
beware often order is important
Some Known ordering constraints :
- rootdse must be first , as it makes redirects from " " - > cn = rootdse
- extended_dn_in must be before objectclass . c , as it resolves the DN
2010-06-07 14:58:30 +02:00
- objectclass must be before password_hash and samldb since these LDB
modules require the expanded " objectClass " list
2013-01-02 09:27:51 +11:00
- objectclass must be before descriptor and acl , as both assume that
objectClass values are sorted
2010-06-07 14:58:30 +02:00
- objectclass_attrs must be behind operational in order to see all
2010-06-07 15:02:38 +02:00
attributes ( the operational module protects and therefore
suppresses per default some important ones )
2009-11-23 20:30:35 +11:00
- partition must be last
- each partition has its own module list then
The list is presented here as a set of declarations to show the
stack visually - the code below then handles the creation of the list
based on the parameters loaded from the database .
*/
2013-09-16 19:51:20 -07:00
static const char * modules_list1 [ ] = { " resolve_oids " ,
2009-11-23 20:30:35 +11:00
" rootdse " ,
2015-07-23 12:09:45 +02:00
" dsdb_notification " ,
2012-05-30 10:41:56 -07:00
" schema_load " ,
2009-11-23 20:30:35 +11:00
" lazy_commit " ,
2011-01-13 21:55:11 +03:00
" dirsync " ,
2009-11-23 20:30:35 +11:00
" paged_results " ,
" ranged_results " ,
" anr " ,
" server_sort " ,
" asq " ,
" extended_dn_store " ,
2013-09-16 19:51:20 -07:00
NULL } ;
/* extended_dn_in or extended_dn_in_openldap goes here */
static const char * modules_list1a [ ] = { " objectclass " ,
2009-11-23 20:30:35 +11:00
" descriptor " ,
" acl " ,
2010-12-06 13:53:06 +02:00
" aclread " ,
2009-11-23 20:30:35 +11:00
" samldb " ,
" password_hash " ,
" operational " ,
" instancetype " ,
2010-06-10 11:05:43 +02:00
" objectclass_attrs " ,
2009-11-23 20:30:35 +11:00
NULL } ;
const char * * link_modules ;
2010-04-20 15:35:51 +10:00
static const char * fedora_ds_modules [ ] = {
" rdn_name " , NULL } ;
static const char * openldap_modules [ ] = {
NULL } ;
2009-11-23 20:30:35 +11:00
static const char * tdb_modules_list [ ] = {
2010-04-20 15:35:51 +10:00
" rdn_name " ,
2009-11-23 20:30:35 +11:00
" subtree_delete " ,
2009-12-16 17:14:26 +11:00
" repl_meta_data " ,
" subtree_rename " ,
2009-11-23 20:30:35 +11:00
" linked_attributes " ,
NULL } ;
const char * extended_dn_module ;
const char * extended_dn_module_ldb = " extended_dn_out_ldb " ;
2009-11-24 11:36:34 +11:00
const char * extended_dn_module_fds = " extended_dn_out_fds " ;
const char * extended_dn_module_openldap = " extended_dn_out_openldap " ;
2013-09-16 19:51:20 -07:00
const char * extended_dn_in_module = " extended_dn_in " ;
2009-11-23 20:30:35 +11:00
2014-12-16 10:58:50 +01:00
static const char * modules_list2 [ ] = { " dns_notify " ,
" show_deleted " ,
2009-11-23 20:30:35 +11:00
" new_partition " ,
" partition " ,
NULL } ;
const char * * backend_modules ;
static const char * fedora_ds_backend_modules [ ] = {
2010-10-19 22:29:04 +11:00
" nsuniqueid " , " paged_searches " , " simple_dn " , NULL } ;
2009-11-23 20:30:35 +11:00
static const char * openldap_backend_modules [ ] = {
2013-09-16 14:12:42 -07:00
" entryuuid " , " simple_dn " , NULL } ;
2009-11-23 20:30:35 +11:00
2013-04-11 22:30:27 -07:00
static const char * samba_dsdb_attrs [ ] = { " backendType " , NULL } ;
2013-09-17 14:04:06 -07:00
static const char * partition_attrs [ ] = { " ldapBackend " , NULL } ;
const char * backendType , * backendUrl ;
bool use_sasl_external = false ;
2009-11-23 20:30:35 +11:00
if ( ! tmp_ctx ) {
2010-07-06 13:21:54 +10:00
return ldb_oom ( ldb ) ;
2009-11-23 20:30:35 +11:00
}
2010-12-08 08:22:21 +11:00
ret = ldb_register_samba_handlers ( ldb ) ;
if ( ret ! = LDB_SUCCESS ) {
talloc_free ( tmp_ctx ) ;
return ret ;
}
2009-11-23 20:30:35 +11:00
samba_dsdb_dn = ldb_dn_new ( tmp_ctx , ldb , " @SAMBA_DSDB " ) ;
if ( ! samba_dsdb_dn ) {
talloc_free ( tmp_ctx ) ;
2010-07-06 13:21:54 +10:00
return ldb_oom ( ldb ) ;
2009-11-23 20:30:35 +11:00
}
2013-09-17 14:04:06 -07:00
partition_dn = ldb_dn_new ( tmp_ctx , ldb , DSDB_PARTITION_DN ) ;
if ( ! partition_dn ) {
talloc_free ( tmp_ctx ) ;
return ldb_oom ( ldb ) ;
}
2009-11-23 20:30:35 +11:00
# define CHECK_LDB_RET(check_ret) \
do { \
if ( check_ret ! = LDB_SUCCESS ) { \
talloc_free ( tmp_ctx ) ; \
return check_ret ; \
} \
} while ( 0 )
2010-07-06 03:23:21 +03:00
ret = dsdb_module_search_dn ( module , tmp_ctx , & res , samba_dsdb_dn ,
2011-01-17 13:12:15 +11:00
samba_dsdb_attrs , DSDB_FLAG_NEXT_MODULE , NULL ) ;
2009-11-25 18:49:42 +11:00
if ( ret = = LDB_ERR_NO_SUCH_OBJECT ) {
backendType = " ldb " ;
} else if ( ret = = LDB_SUCCESS ) {
backendType = ldb_msg_find_attr_as_string ( res - > msgs [ 0 ] , " backendType " , " ldb " ) ;
} else {
2009-11-23 20:30:35 +11:00
talloc_free ( tmp_ctx ) ;
return ret ;
}
backend_modules = NULL ;
if ( strcasecmp ( backendType , " ldb " ) = = 0 ) {
extended_dn_module = extended_dn_module_ldb ;
link_modules = tdb_modules_list ;
} else {
2013-09-10 11:38:10 +12:00
struct cli_credentials * cred ;
2013-09-17 14:04:06 -07:00
bool is_ldapi = false ;
ret = dsdb_module_search_dn ( module , tmp_ctx , & res , partition_dn ,
partition_attrs , DSDB_FLAG_NEXT_MODULE , NULL ) ;
if ( ret = = LDB_SUCCESS ) {
backendUrl = ldb_msg_find_attr_as_string ( res - > msgs [ 0 ] , " ldapBackend " , " ldapi:// " ) ;
if ( ! strncasecmp ( backendUrl , " ldapi:// " , sizeof ( " ldapi:// " ) - 1 ) ) {
is_ldapi = true ;
}
} else if ( ret ! = LDB_ERR_NO_SUCH_OBJECT ) {
talloc_free ( tmp_ctx ) ;
return ret ;
}
2009-11-24 11:36:34 +11:00
if ( strcasecmp ( backendType , " fedora-ds " ) = = 0 ) {
2010-04-20 15:35:51 +10:00
link_modules = fedora_ds_modules ;
2009-11-23 20:30:35 +11:00
backend_modules = fedora_ds_backend_modules ;
extended_dn_module = extended_dn_module_fds ;
2009-11-24 11:36:34 +11:00
} else if ( strcasecmp ( backendType , " openldap " ) = = 0 ) {
2010-04-20 15:35:51 +10:00
link_modules = openldap_modules ;
2009-11-23 20:30:35 +11:00
backend_modules = openldap_backend_modules ;
extended_dn_module = extended_dn_module_openldap ;
2013-09-16 19:51:20 -07:00
extended_dn_in_module = " extended_dn_in_openldap " ;
2013-09-17 14:04:06 -07:00
if ( is_ldapi ) {
use_sasl_external = true ;
}
2010-09-10 20:16:29 +10:00
} else {
return ldb_error ( ldb , LDB_ERR_OPERATIONS_ERROR , " invalid backend type " ) ;
2009-11-23 20:30:35 +11:00
}
2010-07-11 23:07:06 +10:00
ret = ldb_set_opaque ( ldb , " readOnlySchema " , ( void * ) 1 ) ;
if ( ret ! = LDB_SUCCESS ) {
ldb_set_errstring ( ldb , " Failed to set readOnlySchema opaque " ) ;
}
2013-09-10 11:38:10 +12:00
cred = ldb_get_opaque ( ldb , " credentials " ) ;
if ( ! cred | | ! cli_credentials_authentication_requested ( cred ) ) {
2013-09-17 14:04:06 -07:00
ret = set_ldap_credentials ( ldb , use_sasl_external ) ;
2013-09-10 11:38:10 +12:00
if ( ret ! = LDB_SUCCESS ) {
return ret ;
}
}
2009-11-23 20:30:35 +11:00
}
# define CHECK_MODULE_LIST \
do { \
if ( ! final_module_list ) { \
talloc_free ( tmp_ctx ) ; \
2010-07-06 13:21:54 +10:00
return ldb_oom ( ldb ) ; \
2009-11-23 20:30:35 +11:00
} \
} while ( 0 )
2013-09-16 19:51:20 -07:00
final_module_list = str_list_copy_const ( tmp_ctx , modules_list1 ) ;
CHECK_MODULE_LIST ;
final_module_list = str_list_add_const ( final_module_list , extended_dn_in_module ) ;
CHECK_MODULE_LIST ;
final_module_list = str_list_append_const ( final_module_list , modules_list1a ) ;
2009-11-23 20:30:35 +11:00
CHECK_MODULE_LIST ;
final_module_list = str_list_append_const ( final_module_list , link_modules ) ;
CHECK_MODULE_LIST ;
final_module_list = str_list_add_const ( final_module_list , extended_dn_module ) ;
CHECK_MODULE_LIST ;
final_module_list = str_list_append_const ( final_module_list , modules_list2 ) ;
CHECK_MODULE_LIST ;
2011-01-17 13:12:15 +11:00
ret = read_at_rootdse_record ( ldb , module , tmp_ctx , & rootdse_msg , NULL ) ;
2009-11-23 20:30:35 +11:00
CHECK_LDB_RET ( ret ) ;
partition_msg = ldb_msg_new ( tmp_ctx ) ;
partition_msg - > dn = ldb_dn_new ( partition_msg , ldb , " @ " DSDB_OPAQUE_PARTITION_MODULE_MSG_OPAQUE_NAME ) ;
ret = prepare_modules_line ( ldb , tmp_ctx ,
rootdse_msg ,
partition_msg , " schemaNamingContext " ,
" schema_data " , backend_modules ) ;
CHECK_LDB_RET ( ret ) ;
ret = prepare_modules_line ( ldb , tmp_ctx ,
rootdse_msg ,
partition_msg , NULL ,
NULL , backend_modules ) ;
CHECK_LDB_RET ( ret ) ;
ret = ldb_set_opaque ( ldb , DSDB_OPAQUE_PARTITION_MODULE_MSG_OPAQUE_NAME , partition_msg ) ;
CHECK_LDB_RET ( ret ) ;
talloc_steal ( ldb , partition_msg ) ;
/* Now prepare the module chain. Oddly, we must give it to ldb_load_modules_list in REVERSE */
for ( len = 0 ; final_module_list [ len ] ; len + + ) { /* noop */ } ;
reverse_module_list = talloc_array ( tmp_ctx , const char * , len + 1 ) ;
if ( ! reverse_module_list ) {
talloc_free ( tmp_ctx ) ;
2010-07-06 13:21:54 +10:00
return ldb_oom ( ldb ) ;
2009-11-23 20:30:35 +11:00
}
for ( i = 0 ; i < len ; i + + ) {
reverse_module_list [ i ] = final_module_list [ ( len - 1 ) - i ] ;
}
reverse_module_list [ i ] = NULL ;
/* The backend (at least until the partitions module
* reconfigures things ) is the next module in the currently
* loaded chain */
2010-11-02 10:41:28 +11:00
backend_module = ldb_module_next ( module ) ;
ret = ldb_module_load_list ( ldb , reverse_module_list , backend_module , & module_chain ) ;
2009-11-23 20:30:35 +11:00
CHECK_LDB_RET ( ret ) ;
talloc_free ( tmp_ctx ) ;
/* Set this as the 'next' module, so that we effectivly append it to module chain */
2010-11-02 10:41:28 +11:00
ldb_module_set_next ( module , module_chain ) ;
2009-11-23 20:30:35 +11:00
return ldb_next_init ( module ) ;
}
2010-11-01 15:28:02 +11:00
static const struct ldb_module_ops ldb_samba_dsdb_module_ops = {
2009-11-23 20:30:35 +11:00
. name = " samba_dsdb " ,
. init_context = samba_dsdb_init ,
} ;
2010-11-01 15:28:02 +11:00
int ldb_samba_dsdb_module_init ( const char * version )
{
2010-11-01 22:30:45 +11:00
LDB_MODULE_CHECK_VERSION ( version ) ;
2010-11-01 15:28:02 +11:00
return ldb_register_module ( & ldb_samba_dsdb_module_ops ) ;
}