Commit cd7f3c8b authored by Kurt Zeilenga's avatar Kurt Zeilenga
Browse files

Sync with HEAD

parent bd5c9628
#ifndef __BACKSQL_H__
#define __BACKSQL_H__
/*
* Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
*
......@@ -9,6 +6,66 @@
* license is available at http://www.OpenLDAP.org/license.html or
* in file LICENSE in the top-level directory of the distribution.
*/
/*
* Copyright 2002, Pierangelo Masarati <ando@OpenLDAP.org>.
* All rights reserved.
*
* This is a modified version of back-sql; the same conditions
* of the above reported Copyright statement, and sigificantly
* the OpenLDAP Public License apply. Credits go to Dmitry
* Kovalev for the initial development of the backend.
*
* This copyright statement cannot be altered.
*/
/*
* The following changes have been addressed:
*
* Enhancements:
* - re-styled code for better readability
* - upgraded backend API to reflect recent changes
* - LDAP schema is checked when loading SQL/LDAP mapping
* - AttributeDescription/ObjectClass pointers used for more efficient
* mapping lookup
* - bervals used where string length is required often
* - atomized write operations by committing at the end of each operation
* and defaulting connection closure to rollback
* - added LDAP access control to write operations
* - fully implemented modrdn (with rdn attrs change, deleteoldrdn,
* access check, parent/children check and more)
* - added parent access control, children control to delete operation
* - added structuralObjectClass operational attribute check and
* value return on search
* - added hasSubordinate operational attribute on demand
* - search limits are appropriately enforced
* - function backsql_strcat() has been made more efficient
* - concat function has been made configurable by means of a pattern
* - added config switches:
* - fail_if_no_mapping write operations fail if there is no mapping
* - has_ldapinfo_dn_ru overrides autodetect
* - concat_pattern a string containing two '?' is used
* (note that "?||?" should be more portable
* than builtin function "CONCAT(?,?)")
* - strcast_func cast of string constants in "SELECT DISTINCT
* statements (needed by PostgreSQL)
* - upper_needs_cast cast the argument of upper when required
* (basically when building dn substring queries)
*
* Todo:
* - add security checks for SQL statements that can be injected (?)
* - re-test with previously supported RDBMs
* - replace dn_ru and so with normalized dn (no need for upper() and so
* in dn match)
* - implement a backsql_normalize() function to replace the upper()
* conversion routines
* - note that subtree deletion, subtree renaming and so could be easily
* implemented (rollback and consistency checks are available :)
* - implement "lastmod" and other operational stuff (ldap_entries table ?)
* - check how to allow multiple operations with one statement, to remove
* BACKSQL_REALLOC_STMT from modify.c (a more recent unixODBC lib?)
*/
#ifndef __BACKSQL_H__
#define __BACKSQL_H__
#include "external.h"
#include "sql-types.h"
......@@ -18,6 +75,12 @@
*/
#define BACKSQL_MAX_DN_LEN 255
/*
* define to enable very extensive trace logging (debug only)
*/
#undef BACKSQL_TRACE
typedef struct {
char *dbhost;
int dbport;
......@@ -26,27 +89,51 @@ typedef struct {
char *dbname;
/*
* SQL condition for subtree searches differs in syntax:
* "LIKE CONCAT('%',?)" or "LIKE '%'+?" or smth else
* "LIKE CONCAT('%',?)" or "LIKE '%'+?" or "LIKE '%'||?"
* or smth else
*/
char *subtree_cond;
char *oc_query,*at_query;
struct berval subtree_cond;
struct berval children_cond;
char *oc_query, *at_query;
char *insentry_query,*delentry_query;
char *id_query;
char *upper_func;
char *strcast_func;
char *has_children_query;
struct berval upper_func;
struct berval upper_func_open;
struct berval upper_func_close;
BerVarray concat_func;
unsigned int bsql_flags;
#define BSQLF_SCHEMA_LOADED 0x0001
#define BSQLF_UPPER_NEEDS_CAST 0x0002
#define BSQLF_CREATE_NEEDS_SELECT 0x0004
#define BSQLF_FAIL_IF_NO_MAPPING 0x0008
#define BSQLF_HAS_LDAPINFO_DN_RU 0x0010
#define BSQLF_DONTCHECK_LDAPINFO_DN_RU 0x0020
#define BSQLF_USE_REVERSE_DN 0x0040
#define BACKSQL_SCHEMA_LOADED(si) \
((si)->bsql_flags & BSQLF_SCHEMA_LOADED)
#define BACKSQL_UPPER_NEEDS_CAST(si) \
((si)->bsql_flags & BSQLF_UPPER_NEEDS_CAST)
#define BACKSQL_CREATE_NEEDS_SELECT(si) \
((si)->bsql_flags & BSQLF_CREATE_NEEDS_SELECT)
#define BACKSQL_FAIL_IF_NO_MAPPING(si) \
((si)->bsql_flags & BSQLF_FAIL_IF_NO_MAPPING)
#define BACKSQL_HAS_LDAPINFO_DN_RU(si) \
((si)->bsql_flags & BSQLF_HAS_LDAPINFO_DN_RU)
#define BACKSQL_DONTCHECK_LDAPINFO_DN_RU(si) \
((si)->bsql_flags & BSQLF_DONTCHECK_LDAPINFO_DN_RU)
#define BACKSQL_USE_REVERSE_DN(si) \
((si)->bsql_flags & BSQLF_USE_REVERSE_DN)
struct berval strcast_func;
Avlnode *db_conns;
Avlnode *oc_by_oc;
Avlnode *oc_by_id;
int schema_loaded;
ldap_pvt_thread_mutex_t dbconn_mutex;
ldap_pvt_thread_mutex_t schema_mutex;
SQLHENV db_env;
int isTimesTen;
/*
* Does ldapinfo.dn_ru exist in schema?
*/
int has_ldapinfo_dn_ru;
} backsql_info;
#define BACKSQL_SUCCESS( rc ) \
......
......@@ -17,6 +17,7 @@
#include "slap.h"
#include "back-sql.h"
#include "sql-wrap.h"
#include "util.h"
int
backsql_db_config(
......@@ -35,7 +36,7 @@ backsql_db_config(
if ( argc < 2 ) {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing hostname in dbhost directive\n",
"missing hostname in \"dbhost\" directive\n",
fname, lineno, 0 );
return 1;
}
......@@ -48,7 +49,7 @@ backsql_db_config(
if ( argc < 2 ) {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing username in dbuser directive\n",
"missing username in \"dbuser\" directive\n",
fname, lineno, 0 );
return 1;
}
......@@ -60,7 +61,7 @@ backsql_db_config(
if ( argc < 2 ) {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing password in dbpasswd directive\n",
"missing password in \"dbpasswd\" directive\n",
fname, lineno, 0 );
return 1;
}
......@@ -72,33 +73,53 @@ backsql_db_config(
if ( argc < 2 ) {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing database name in dbname directive\n",
fname, lineno, 0 );
"missing database name in \"dbname\" "
"directive\n", fname, lineno, 0 );
return 1;
}
si->dbname = ch_strdup( argv[ 1 ] );
Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): dbname=%s\n",
si->dbname, 0, 0 );
} else if ( !strcasecmp( argv[ 0 ], "concat_pattern" ) ) {
if ( argc < 2 ) {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing pattern"
"in \"concat_pattern\" directive\n",
fname, lineno, 0 );
return 1;
}
if ( backsql_split_pattern( argv[ 1 ], &si->concat_func, 2 ) ) {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"unable to parse pattern \"%s\"\n"
"in \"concat_pattern\" directive\n",
fname, lineno, argv[ 1 ] );
return 1;
}
Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
"concat_pattern=\"%s\"\n", argv[ 1 ], 0, 0 );
} else if ( !strcasecmp( argv[ 0 ], "subtree_cond" ) ) {
if ( argc < 2 ) {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing SQL condition "
"in subtree_cond directive\n",
"in \"subtree_cond\" directive\n",
fname, lineno, 0 );
return 1;
}
si->subtree_cond = ch_strdup( argv[ 1 ] );
ber_str2bv( argv[ 1 ], 0, 1, &si->subtree_cond );
Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
"subtree_cond=%s\n", si->subtree_cond, 0, 0 );
"subtree_cond=%s\n", si->subtree_cond.bv_val, 0, 0 );
} else if ( !strcasecmp( argv[ 0 ], "oc_query" ) ) {
if ( argc < 2 ) {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing SQL statement "
"in oc_query directive\n",
"in \"oc_query\" directive\n",
fname, lineno, 0 );
return 1;
}
......@@ -111,7 +132,7 @@ backsql_db_config(
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing SQL statement "
"in at_query directive\n",
"in \"at_query\" directive\n",
fname, lineno, 0 );
return 1;
}
......@@ -124,7 +145,7 @@ backsql_db_config(
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing SQL statement "
"in insentry_query directive\n",
"in \"insentry_query\" directive\n",
fname, lineno, 0 );
return 1;
}
......@@ -132,38 +153,97 @@ backsql_db_config(
Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
"insentry_query=%s\n", si->insentry_query, 0, 0 );
} else if ( !strcasecmp( argv[ 0 ], "create_needs_select" ) ) {
if ( argc < 2 ) {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing { yes | no }"
"in \"create_needs_select\" directive\n",
fname, lineno, 0 );
return 1;
}
if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
si->bsql_flags |= BSQLF_CREATE_NEEDS_SELECT;
} else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
si->bsql_flags &= ~BSQLF_CREATE_NEEDS_SELECT;
} else {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"\"create_needs_select\" directive arg "
"must be \"yes\" or \"no\"\n",
fname, lineno, 0 );
return 1;
}
Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
"create_needs_select =%s\n",
BACKSQL_CREATE_NEEDS_SELECT( si ) ? "yes" : "no",
0, 0 );
} else if ( !strcasecmp( argv[ 0 ], "upper_func" ) ) {
if ( argc < 2 ) {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing function name "
"in upper_func directive\n",
"in \"upper_func\" directive\n",
fname, lineno, 0 );
return 1;
}
si->upper_func = ch_strdup( argv[ 1 ] );
ber_str2bv( argv[ 1 ], 0, 1, &si->upper_func );
Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
"upper_func=%s\n", si->upper_func, 0, 0 );
"upper_func=%s\n", si->upper_func.bv_val, 0, 0 );
} else if ( !strcasecmp( argv[ 0 ], "upper_needs_cast" ) ) {
if ( argc < 2 ) {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing { yes | no }"
"in \"upper_needs_cast\" directive\n",
fname, lineno, 0 );
return 1;
}
if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
si->bsql_flags |= BSQLF_UPPER_NEEDS_CAST;
} else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
si->bsql_flags &= ~BSQLF_UPPER_NEEDS_CAST;
} else {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"\"upper_needs_cast\" directive arg "
"must be \"yes\" or \"no\"\n",
fname, lineno, 0 );
return 1;
}
Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
"upper_needs_cast =%s\n",
BACKSQL_UPPER_NEEDS_CAST( si ) ? "yes" : "no", 0, 0 );
} else if ( !strcasecmp( argv[ 0 ], "strcast_func" ) ) {
if ( argc < 2 ) {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing function name "
"in strcast_func directive\n",
"in \"strcast_func\" directive\n",
fname, lineno, 0 );
return 1;
}
si->strcast_func = ch_strdup( argv[ 1 ] );
ber_str2bv( argv[ 1 ], 0, 1, &si->strcast_func );
Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
"strcast_func=%s\n", si->strcast_func, 0, 0 );
"strcast_func=%s\n", si->strcast_func.bv_val, 0, 0 );
} else if ( !strcasecmp( argv[ 0 ], "delentry_query" ) ) {
if ( argc < 2 ) {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing SQL statement "
"in delentry_query directive\n",
"in \"delentry_query\" directive\n",
fname, lineno, 0 );
return 1;
}
......@@ -176,19 +256,23 @@ backsql_db_config(
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing { yes | no }"
"in has_ldapinfo_dn_ru directive\n",
"in \"has_ldapinfo_dn_ru\" directive\n",
fname, lineno, 0 );
return 1;
}
if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
si->has_ldapinfo_dn_ru = 1;
si->bsql_flags |= BSQLF_HAS_LDAPINFO_DN_RU;
si->bsql_flags |= BSQLF_DONTCHECK_LDAPINFO_DN_RU;
} else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
si->has_ldapinfo_dn_ru = 0;
si->bsql_flags &= ~BSQLF_HAS_LDAPINFO_DN_RU;
si->bsql_flags |= BSQLF_DONTCHECK_LDAPINFO_DN_RU;
} else {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"has_ldapinfo_dn_ru directive arg "
"\"has_ldapinfo_dn_ru\" directive arg "
"must be \"yes\" or \"no\"\n",
fname, lineno, 0 );
return 1;
......@@ -196,11 +280,40 @@ backsql_db_config(
}
Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
"has_ldapinfo_dn_ru=%s\n",
si->has_ldapinfo_dn_ru == 0 ? "no" : "yes", 0, 0 );
BACKSQL_HAS_LDAPINFO_DN_RU( si ) ? "yes" : "no", 0, 0 );
} else if ( !strcasecmp( argv[ 0 ], "fail_if_no_mapping") ) {
if ( argc < 2 ) {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"missing { yes | no }"
"in \"fail_if_no_mapping\" directive\n",
fname, lineno, 0 );
return 1;
}
if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
si->bsql_flags |= BSQLF_FAIL_IF_NO_MAPPING;
} else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
si->bsql_flags &= ~BSQLF_FAIL_IF_NO_MAPPING;
} else {
Debug( LDAP_DEBUG_TRACE,
"<==backsql_db_config (%s line %d): "
"\"fail_if_no_mapping\" directive arg "
"must be \"yes\" or \"no\"\n",
fname, lineno, 0 );
return 1;
}
Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
"fail_if_no_mapping=%s\n",
BACKSQL_FAIL_IF_NO_MAPPING( si ) ? "yes" : "no", 0, 0 );
} else {
Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config (%s line %d): "
"unknown directive '%s' (ignored)\n",
"unknown directive \"%s\" (ignored)\n",
fname, lineno, argv[ 0 ] );
}
......
......@@ -42,11 +42,6 @@ backsql_free_entryID( backsql_entryID *id, int freeit )
return next;
}
/*
* FIXME: need to change API to pass backsql_entryID **id
* and return an error code, to distinguish LDAP_OTHER from
* LDAP_NO_SUCH_OBJECT
*/
int
backsql_dn2id(
backsql_info *bi,
......@@ -56,9 +51,6 @@ backsql_dn2id(
{
SQLHSTMT sth;
BACKSQL_ROW_NTS row;
#if 0
SQLINTEGER nrows = 0;
#endif
RETCODE rc;
int res;
......@@ -93,7 +85,7 @@ backsql_dn2id(
return LDAP_OTHER;
}
if ( bi->has_ldapinfo_dn_ru ) {
if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
/*
* Prepare an upper cased, byte reversed version
* that can be searched using indexes
......@@ -109,7 +101,7 @@ backsql_dn2id(
upperdn, 0, 0 );
toBind = upperdn;
} else {
if ( bi->isTimesTen ) {
if ( BACKSQL_USE_REVERSE_DN( bi ) ) {
AC_MEMCPY( upperdn, dn->bv_val, dn->bv_len + 1 );
ldap_pvt_str2upper( upperdn );
Debug( LDAP_DEBUG_TRACE,
......@@ -126,7 +118,8 @@ backsql_dn2id(
if ( rc != SQL_SUCCESS) {
/* end TimesTen */
Debug( LDAP_DEBUG_TRACE, "backsql_dn2id(): "
"error binding dn=\"%s\" parameter:\n", toBind, 0, 0 );
"error binding dn=\"%s\" parameter:\n",
toBind, 0, 0 );
backsql_PrintErrors( SQL_NULL_HENV, dbh, sth, rc );
SQLFreeStmt( sth, SQL_DROP );
return LDAP_OTHER;
......@@ -145,9 +138,9 @@ backsql_dn2id(
backsql_BindRowAsStrings( sth, &row );
rc = SQLFetch( sth );
if ( BACKSQL_SUCCESS( rc ) ) {
id->id = atoi( row.cols[ 0 ] );
id->keyval = atoi( row.cols[ 1 ] );
id->oc_id = atoi( row.cols[ 2 ] );
id->id = strtol( row.cols[ 0 ], NULL, 0 );
id->keyval = strtol( row.cols[ 1 ], NULL, 0 );
id->oc_id = strtol( row.cols[ 2 ], NULL, 0 );
ber_dupbv( &id->dn, dn );
id->next = NULL;
......@@ -169,6 +162,86 @@ backsql_dn2id(
return res;
}
int
backsql_has_children(
backsql_info *bi,
SQLHDBC dbh,
struct berval *dn )
{
SQLHSTMT sth;
BACKSQL_ROW_NTS row;
RETCODE rc;
int res;
Debug( LDAP_DEBUG_TRACE, "==>backsql_has_children(): dn='%s'\n",
dn->bv_val, 0, 0 );
if ( dn->bv_len > BACKSQL_MAX_DN_LEN ) {
Debug( LDAP_DEBUG_TRACE,
"backsql_has_children(): DN \"%s\" (%ld bytes) "
"exceeds max DN length (%d):\n",
dn->bv_val, dn->bv_len, BACKSQL_MAX_DN_LEN );
return LDAP_OTHER;
}
/* begin TimesTen */
Debug(LDAP_DEBUG_TRACE, "children id query '%s'\n",
bi->has_children_query, 0, 0);
assert( bi->has_children_query );
rc = backsql_Prepare( dbh, &sth, bi->has_children_query, 0 );
if ( rc != SQL_SUCCESS ) {
Debug( LDAP_DEBUG_TRACE,
"backsql_has_children(): error preparing SQL:\n%s",
bi->has_children_query, 0, 0);
backsql_PrintErrors( SQL_NULL_HENV, dbh, sth, rc );
SQLFreeStmt( sth, SQL_DROP );
return LDAP_OTHER;
}
rc = backsql_BindParamStr( sth, 1, dn->bv_val, BACKSQL_MAX_DN_LEN );
if ( rc != SQL_SUCCESS) {
/* end TimesTen */
Debug( LDAP_DEBUG_TRACE, "backsql_has_children(): "
"error binding dn=\"%s\" parameter:\n",
dn->bv_val, 0, 0 );
backsql_PrintErrors( SQL_NULL_HENV, dbh, sth, rc );
SQLFreeStmt( sth, SQL_DROP );
return LDAP_OTHER;
}
rc = SQLExecute( sth );
if ( rc != SQL_SUCCESS ) {
Debug( LDAP_DEBUG_TRACE, "backsql_has_children(): "
"error executing query (\"%s\", \"%s\"):\n",
bi->has_children_query, dn->bv_val, 0 );
backsql_PrintErrors( SQL_NULL_HENV, dbh, sth, rc );
SQLFreeStmt( sth, SQL_DROP );
return LDAP_OTHER;
}
backsql_BindRowAsStrings( sth, &row );
rc = SQLFetch( sth );
if ( BACKSQL_SUCCESS( rc ) ) {
if ( strtol( row.cols[ 0 ], NULL, 0 ) > 0 ) {
res = LDAP_COMPARE_TRUE;
} else {
res = LDAP_COMPARE_FALSE;
}
} else {
res = LDAP_OTHER;
}
backsql_FreeRow( &row );
SQLFreeStmt( sth, SQL_DROP );
Debug( LDAP_DEBUG_TRACE, "<==backsql_has_children(): %s\n",
res == LDAP_COMPARE_TRUE ? "yes" : "no", 0, 0 );
return res;
}
int
backsql_get_attr_vals( backsql_at_map_rec *at, backsql_srch_info *bsi )
{
......@@ -182,7 +255,9 @@ backsql_get_attr_vals( backsql_at_map_rec *at, backsql_srch_info *bsi )
Debug( LDAP_DEBUG_TRACE, "==>backsql_get_attr_vals(): "
"oc='%s' attr='%s' keyval=%ld\n",
bsi->oc->name.bv_val, at->name.bv_val, bsi->c_eid->keyval );
// bsi->oc->name.bv_val, at->name.bv_val,
bsi->oc->oc->soc_names[0], at->ad->ad_cname.bv_val,
bsi->c_eid->keyval );
rc = backsql_Prepare( bsi->dbh, &sth, at->query, 0 );
if ( rc != SQL_SUCCESS ) {
......@@ -230,14 +305,14 @@ backsql_get_attr_vals( backsql_at_map_rec *at, backsql_srch_info *bsi )
backsql_entry_addattr( bsi->e,
&row.col_names[ i ], &bv );
#if 0
#ifdef BACKSQL_TRACE
Debug( LDAP_DEBUG_TRACE, "prec=%d\n",
(int)row.col_prec[ i ], 0, 0 );
} else {
Debug( LDAP_DEBUG_TRACE, "NULL value "
"in this row for attribute '%s'\n",
row.col_names[ i ].bv_val, 0, 0 );
#endif
#endif /* BACKSQL_TRACE */
}
}
}
......@@ -255,6 +330,7 @@ backsql_id2entry( backsql_srch_info *bsi, Entry *e, backsql_entryID *eid )
int i;
backsql_at_map_rec *at;
int rc;