openldap/servers/lloadd/config.c
Ondřej Kuzník 1f6d8611a3 Implement read throttling when writes backlog
Reject operations in such a case with LDAP_BUSY. If read_event feature
is on, just stop reading from the connection. However this could still
result in deadlocks in reasonable situations. Need to figure out better
ways to make it safe and still protect ourselves.
2020-11-17 17:58:15 +00:00

3793 lines
108 KiB
C

/* config.c - configuration file handling routines */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2020 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/* Portions Copyright (c) 1995 Regents of the University of Michigan.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that this notice is preserved and that due credit is given
* to the University of Michigan at Ann Arbor. The name of the University
* may not be used to endorse or promote products derived from this
* software without specific prior written permission. This software
* is provided ``as is'' without express or implied warranty.
*/
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include <ac/ctype.h>
#include <ac/signal.h>
#include <ac/socket.h>
#include <ac/errno.h>
#include <ac/unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef S_ISREG
#define S_ISREG(m) ( ((m) & _S_IFMT ) == _S_IFREG )
#endif
#include "lload.h"
#include "lutil.h"
#include "lutil_ldap.h"
#include "config.h"
#ifdef _WIN32
#define LUTIL_ATOULX lutil_atoullx
#define Z "I"
#else
#define LUTIL_ATOULX lutil_atoulx
#define Z "z"
#endif
#define ARGS_STEP 512
/*
* defaults for various global variables
*/
#ifdef BALANCER_MODULE
char *listeners_list = NULL;
#else /* !BALANCER_MODULE */
slap_mask_t global_allows = 0;
slap_mask_t global_disallows = 0;
int global_gentlehup = 0;
int global_idletimeout = 0;
char *global_host = NULL;
char *slapd_pid_file = NULL;
char *slapd_args_file = NULL;
#endif /* !BALANCER_MODULE */
static FILE *logfile;
static char *logfileName;
static struct timeval timeout_api_tv, timeout_net_tv,
timeout_write_tv = { 10, 0 };
lload_features_t lload_features;
ber_len_t sockbuf_max_incoming_client = LLOAD_SB_MAX_INCOMING_CLIENT;
ber_len_t sockbuf_max_incoming_upstream = LLOAD_SB_MAX_INCOMING_UPSTREAM;
int lload_conn_max_pdus_per_cycle = LLOAD_CONN_MAX_PDUS_PER_CYCLE_DEFAULT;
struct timeval *lload_timeout_api = NULL;
struct timeval *lload_timeout_net = NULL;
struct timeval *lload_write_timeout = &timeout_write_tv;
static slap_verbmasks tlskey[];
static int fp_getline( FILE *fp, ConfigArgs *c );
static void fp_getline_init( ConfigArgs *c );
static char *strtok_quote(
char *line,
char *sep,
char **quote_ptr,
int *inquote );
typedef struct ConfigFile {
struct ConfigFile *c_sibs;
struct ConfigFile *c_kids;
struct berval c_file;
BerVarray c_dseFiles;
} ConfigFile;
static ConfigFile *cfn;
static ConfigDriver config_fname;
static ConfigDriver config_generic;
static ConfigDriver config_backend;
static ConfigDriver config_bindconf;
#ifdef LDAP_TCP_BUFFER
static ConfigDriver config_tcp_buffer;
#endif /* LDAP_TCP_BUFFER */
static ConfigDriver config_restrict;
static ConfigDriver config_loglevel;
static ConfigDriver config_include;
static ConfigDriver config_feature;
#ifdef HAVE_TLS
static ConfigDriver config_tls_option;
static ConfigDriver config_tls_config;
#endif
#ifdef BALANCER_MODULE
static ConfigDriver config_share_tls_ctx;
static ConfigDriver backend_cf_gen;
#endif /* BALANCER_MODULE */
lload_b_head backend = LDAP_CIRCLEQ_HEAD_INITIALIZER(backend);
ldap_pvt_thread_mutex_t backend_mutex;
LloadBackend *current_backend = NULL;
struct slap_bindconf bindconf = {};
struct berval lloadd_identity = BER_BVNULL;
enum {
CFG_ACL = 1,
CFG_BACKEND,
CFG_BINDCONF,
CFG_LISTEN,
CFG_LISTEN_URI,
CFG_TLS_RAND,
CFG_TLS_CIPHER,
CFG_TLS_PROTOCOL_MIN,
CFG_TLS_CERT_FILE,
CFG_TLS_CERT_KEY,
CFG_TLS_CA_PATH,
CFG_TLS_CA_FILE,
CFG_TLS_DH_FILE,
CFG_TLS_VERIFY,
CFG_TLS_CRLCHECK,
CFG_TLS_CRL_FILE,
CFG_TLS_SHARE_CTX,
CFG_CONCUR,
CFG_THREADS,
CFG_LOGFILE,
CFG_MIRRORMODE,
CFG_IOTHREADS,
CFG_MAXBUF_CLIENT,
CFG_MAXBUF_UPSTREAM,
CFG_FEATURE,
CFG_THREADQS,
CFG_TLS_ECNAME,
CFG_TLS_CACERT,
CFG_TLS_CERT,
CFG_TLS_KEY,
CFG_RESCOUNT,
CFG_IOTIMEOUT,
CFG_URI,
CFG_NUMCONNS,
CFG_BINDCONNS,
CFG_RETRY,
CFG_MAX_PENDING_OPS,
CFG_MAX_PENDING_CONNS,
CFG_STARTTLS,
CFG_CLIENT_PENDING,
CFG_LAST
};
/* alphabetical ordering */
static ConfigTable config_back_cf_table[] = {
/* This attr is read-only */
{ "", "", 0, 0, 0,
ARG_MAGIC,
&config_fname,
NULL, NULL, NULL
},
{ "argsfile", "file", 2, 2, 0,
ARG_STRING,
&slapd_args_file,
NULL, NULL, NULL
},
{ "concurrency", "level", 2, 2, 0,
ARG_UINT|ARG_MAGIC|CFG_CONCUR,
&config_generic,
NULL, NULL, NULL
},
/* conf-file only option */
{ "backend-server", "backend options", 2, 0, 0,
ARG_MAGIC|CFG_BACKEND,
&config_backend,
NULL, NULL, NULL
},
{ "bindconf", "backend credentials", 2, 0, 0,
ARG_MAGIC|CFG_BINDCONF,
&config_bindconf,
"( OLcfgBkAt:13.2 "
"NAME 'olcBkLloadBindconf' "
"DESC 'Backend credentials' "
/* No EQUALITY since this is a compound attribute (and needs
* splitting up anyway - which is a TODO) */
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "gentlehup", "on|off", 2, 2, 0,
#ifdef SIGHUP
ARG_ON_OFF,
&global_gentlehup,
#else
ARG_IGNORED,
NULL,
#endif
NULL, NULL, NULL
},
{ "idletimeout", "timeout", 2, 2, 0,
ARG_UINT,
&global_idletimeout,
"( OLcfgBkAt:13.3 "
"NAME 'olcBkLloadIdleTimeout' "
"DESC 'Connection idle timeout' "
"EQUALITY integerMatch "
"SYNTAX OMsInteger "
"SINGLE-VALUE )",
NULL, NULL
},
{ "include", "file", 2, 2, 0,
ARG_MAGIC,
&config_include,
NULL, NULL, NULL
},
{ "io-threads", "count", 2, 0, 0,
ARG_UINT|ARG_MAGIC|CFG_IOTHREADS,
&config_generic,
"( OLcfgBkAt:13.4 "
"NAME 'olcBkLloadIOThreads' "
"DESC 'I/O thread count' "
"EQUALITY integerMatch "
"SYNTAX OMsInteger "
"SINGLE-VALUE )",
NULL, NULL
},
#ifdef BALANCER_MODULE
{ "listen", "uri list", 2, 2, 0,
ARG_STRING|ARG_MAGIC|CFG_LISTEN,
&config_generic,
NULL, NULL, NULL
},
{ "", "uri", 2, 2, 0,
ARG_MAGIC|CFG_LISTEN_URI,
&config_generic,
"( OLcfgBkAt:13.5 "
"NAME 'olcBkLloadListen' "
"DESC 'A listener adress' "
/* We don't handle adding/removing a value, so no EQUALITY yet */
"SYNTAX OMsDirectoryString )",
NULL, NULL
},
#endif /* BALANCER_MODULE */
{ "logfile", "file", 2, 2, 0,
ARG_STRING|ARG_MAGIC|CFG_LOGFILE,
&config_generic,
NULL, NULL, NULL
},
{ "loglevel", "level", 2, 0, 0,
ARG_MAGIC,
&config_loglevel,
NULL, NULL, NULL
},
{ "pidfile", "file", 2, 2, 0,
ARG_STRING,
&slapd_pid_file,
NULL, NULL, NULL
},
{ "restrict", "op_list", 2, 0, 0,
ARG_MAGIC,
&config_restrict,
NULL, NULL, NULL
},
{ "sockbuf_max_incoming_client", "max", 2, 2, 0,
ARG_BER_LEN_T|ARG_MAGIC|CFG_MAXBUF_CLIENT,
&config_generic,
"( OLcfgBkAt:13.6 "
"NAME 'olcBkLloadSockbufMaxClient' "
"DESC 'The maximum LDAP PDU size accepted coming from clients' "
"EQUALITY integerMatch "
"SYNTAX OMsInteger "
"SINGLE-VALUE )",
NULL, NULL
},
{ "sockbuf_max_incoming_upstream", "max", 2, 2, 0,
ARG_BER_LEN_T|ARG_MAGIC|CFG_MAXBUF_UPSTREAM,
&config_generic,
"( OLcfgBkAt:13.7 "
"NAME 'olcBkLloadSockbufMaxUpstream' "
"DESC 'The maximum LDAP PDU size accepted coming from upstream' "
"EQUALITY integerMatch "
"SYNTAX OMsInteger "
"SINGLE-VALUE )",
NULL, NULL
},
{ "tcp-buffer", "[listener=<listener>] [{read|write}=]size", 0, 0, 0,
#ifdef LDAP_TCP_BUFFER
ARG_MAGIC,
&config_tcp_buffer,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.8 "
"NAME 'olcBkLloadTcpBuffer' "
"DESC 'TCP Buffer size' "
"EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "threads", "count", 2, 2, 0,
ARG_UINT|ARG_MAGIC|CFG_THREADS,
&config_generic,
NULL, NULL, NULL
},
{ "threadqueues", "count", 2, 2, 0,
ARG_UINT|ARG_MAGIC|CFG_THREADQS,
&config_generic,
NULL, NULL, NULL
},
{ "max_pdus_per_cycle", "count", 2, 2, 0,
ARG_UINT|ARG_MAGIC|CFG_RESCOUNT,
&config_generic,
"( OLcfgBkAt:13.9 "
"NAME 'olcBkLloadMaxPDUPerCycle' "
"DESC 'Maximum number of PDUs to handle in a single cycle' "
"EQUALITY integerMatch "
"SYNTAX OMsInteger "
"SINGLE-VALUE )",
NULL, NULL
},
{ "feature", "name", 2, 0, 0,
ARG_MAGIC|CFG_FEATURE,
&config_feature,
"( OLcfgBkAt:13.10 "
"NAME 'olcBkLloadFeature' "
"DESC 'Lload features enabled' "
"EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString )",
NULL, NULL
},
{ "TLSCACertificate", NULL, 2, 2, 0,
#ifdef HAVE_TLS
CFG_TLS_CACERT|ARG_BINARY|ARG_MAGIC,
&config_tls_option,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.11 "
"NAME 'olcBkLloadTLSCACertificate' "
"DESC 'X.509 certificate, must use ;binary' "
"EQUALITY certificateExactMatch "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSCACertificateFile", NULL, 2, 2, 0,
#ifdef HAVE_TLS
CFG_TLS_CA_FILE|ARG_STRING|ARG_MAGIC,
&config_tls_option,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.12 "
"NAME 'olcBkLloadTLSCACertificateFile' "
"EQUALITY caseExactMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSCACertificatePath", NULL, 2, 2, 0,
#ifdef HAVE_TLS
CFG_TLS_CA_PATH|ARG_STRING|ARG_MAGIC,
&config_tls_option,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.13 "
"NAME 'olcBkLloadTLSCACertificatePath' "
"EQUALITY caseExactMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSCertificate", NULL, 2, 2, 0,
#ifdef HAVE_TLS
CFG_TLS_CERT|ARG_BINARY|ARG_MAGIC,
&config_tls_option,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.14 "
"NAME 'olcBkLloadTLSCertificate' "
"DESC 'X.509 certificate, must use ;binary' "
"EQUALITY certificateExactMatch "
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSCertificateFile", NULL, 2, 2, 0,
#ifdef HAVE_TLS
CFG_TLS_CERT_FILE|ARG_STRING|ARG_MAGIC,
&config_tls_option,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.15 "
"NAME 'olcBkLloadTLSCertificateFile' "
"EQUALITY caseExactMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSCertificateKey", NULL, 2, 2, 0,
#ifdef HAVE_TLS
CFG_TLS_KEY|ARG_BINARY|ARG_MAGIC,
&config_tls_option,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.16 "
"NAME 'olcBkLloadTLSCertificateKey' "
"DESC 'X.509 privateKey, must use ;binary' "
"EQUALITY privateKeyMatch "
"SYNTAX 1.2.840.113549.1.8.1.1 "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSCertificateKeyFile", NULL, 2, 2, 0,
#ifdef HAVE_TLS
CFG_TLS_CERT_KEY|ARG_STRING|ARG_MAGIC,
&config_tls_option,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.17 "
"NAME 'olcBkLloadTLSCertificateKeyFile' "
"EQUALITY caseExactMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSCipherSuite", NULL, 2, 2, 0,
#ifdef HAVE_TLS
CFG_TLS_CIPHER|ARG_STRING|ARG_MAGIC,
&config_tls_option,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.18 "
"NAME 'olcBkLloadTLSCipherSuite' "
"EQUALITY caseExactMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSCRLCheck", NULL, 2, 2, 0,
#if defined(HAVE_TLS) && defined(HAVE_OPENSSL)
CFG_TLS_CRLCHECK|ARG_STRING|ARG_MAGIC,
&config_tls_config,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.19 "
"NAME 'olcBkLloadTLSCRLCheck' "
"EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSCRLFile", NULL, 2, 2, 0,
#if defined(HAVE_GNUTLS)
CFG_TLS_CRL_FILE|ARG_STRING|ARG_MAGIC,
&config_tls_option,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.20 "
"NAME 'olcBkLloadTLSCRLFile' "
"EQUALITY caseExactMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSRandFile", NULL, 2, 2, 0,
#ifdef HAVE_TLS
CFG_TLS_RAND|ARG_STRING|ARG_MAGIC,
&config_tls_option,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.21 "
"NAME 'olcBkLloadTLSRandFile' "
"EQUALITY caseExactMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSVerifyClient", NULL, 2, 2, 0,
#ifdef HAVE_TLS
CFG_TLS_VERIFY|ARG_STRING|ARG_MAGIC,
&config_tls_config,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.22 "
"NAME 'olcBkLloadVerifyClient' "
"EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSDHParamFile", NULL, 2, 2, 0,
#ifdef HAVE_TLS
CFG_TLS_DH_FILE|ARG_STRING|ARG_MAGIC,
&config_tls_option,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.23 "
"NAME 'olcBkLloadTLSDHParamFile' "
"EQUALITY caseExactMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSECName", NULL, 2, 2, 0,
#ifdef HAVE_TLS
CFG_TLS_ECNAME|ARG_STRING|ARG_MAGIC,
&config_tls_option,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.24 "
"NAME 'olcBkLloadTLSECName' "
"EQUALITY caseExactMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSProtocolMin", NULL, 2, 2, 0,
#ifdef HAVE_TLS
CFG_TLS_PROTOCOL_MIN|ARG_STRING|ARG_MAGIC,
&config_tls_config,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.25 "
"NAME 'olcBkLloadTLSProtocolMin' "
"EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "TLSShareSlapdCTX", NULL, 2, 2, 0,
#if defined(HAVE_TLS) && defined(BALANCER_MODULE)
CFG_TLS_SHARE_CTX|ARG_ON_OFF|ARG_MAGIC,
&config_share_tls_ctx,
#else
ARG_IGNORED,
NULL,
#endif
"( OLcfgBkAt:13.33 "
"NAME 'olcBkLloadTLSShareSlapdCTX' "
"DESC 'Share slapd TLS context (all other lloadd TLS options cease to take effect)' "
"EQUALITY booleanMatch "
"SYNTAX OMsBoolean "
"SINGLE-VALUE )",
NULL, NULL
},
{ "iotimeout", "ms timeout", 2, 2, 0,
ARG_UINT|ARG_MAGIC|CFG_IOTIMEOUT,
&config_generic,
"( OLcfgBkAt:13.26 "
"NAME 'olcBkLloadIOTimeout' "
"DESC 'I/O timeout threshold in miliseconds' "
"EQUALITY integerMatch "
"SYNTAX OMsInteger "
"SINGLE-VALUE )",
NULL, NULL
},
{ "client_max_pending", NULL, 2, 2, 0,
ARG_MAGIC|ARG_UINT|CFG_CLIENT_PENDING,
&config_generic,
"( OLcfgBkAt:13.35 "
"NAME 'olcBkLloadClientMaxPending' "
"DESC 'Maximum pending operations per client connection' "
"EQUALITY integerMatch "
"SYNTAX OMsInteger "
"SINGLE-VALUE )",
NULL, NULL
},
/* cn=config only options */
#ifdef BALANCER_MODULE
{ "", "uri", 2, 2, 0,
ARG_BERVAL|ARG_MAGIC|CFG_URI,
&backend_cf_gen,
"( OLcfgBkAt:13.27 "
"NAME 'olcBkLloadBackendUri' "
"DESC 'URI to contact the server on' "
"EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
{ "", NULL, 2, 2, 0,
ARG_UINT|ARG_MAGIC|CFG_NUMCONNS,
&backend_cf_gen,
"( OLcfgBkAt:13.28 "
"NAME 'olcBkLloadNumconns' "
"DESC 'Number of regular connections to maintain' "
"EQUALITY integerMatch "
"SYNTAX OMsInteger "
"SINGLE-VALUE )",
NULL, NULL
},
{ "", NULL, 2, 2, 0,
ARG_UINT|ARG_MAGIC|CFG_BINDCONNS,
&backend_cf_gen,
"( OLcfgBkAt:13.29 "
"NAME 'olcBkLloadBindconns' "
"DESC 'Number of bind connections to maintain' "
"EQUALITY integerMatch "
"SYNTAX OMsInteger "
"SINGLE-VALUE )",
NULL, NULL
},
{ "", NULL, 2, 2, 0,
ARG_UINT|ARG_MAGIC|CFG_RETRY,
&backend_cf_gen,
"( OLcfgBkAt:13.30 "
"NAME 'olcBkLloadRetry' "
"DESC 'Number of seconds to wait before trying to reconnect' "
"EQUALITY integerMatch "
"SYNTAX OMsInteger "
"SINGLE-VALUE )",
NULL, NULL
},
{ "", NULL, 2, 2, 0,
ARG_UINT|ARG_MAGIC|CFG_MAX_PENDING_OPS,
&backend_cf_gen,
"( OLcfgBkAt:13.31 "
"NAME 'olcBkLloadMaxPendingOps' "
"DESC 'Maximum number of pending operations for this backend' "
"EQUALITY integerMatch "
"SYNTAX OMsInteger "
"SINGLE-VALUE )",
NULL, NULL
},
{ "", NULL, 2, 2, 0,
ARG_UINT|ARG_MAGIC|CFG_MAX_PENDING_CONNS,
&backend_cf_gen,
"( OLcfgBkAt:13.32 "
"NAME 'olcBkLloadMaxPendingConns' "
"DESC 'Maximum number of pending operations on each connection' "
"EQUALITY integerMatch "
"SYNTAX OMsInteger "
"SINGLE-VALUE )",
NULL, NULL
},
{ "", NULL, 2, 2, 0,
ARG_BERVAL|ARG_MAGIC|CFG_STARTTLS,
&backend_cf_gen,
"( OLcfgBkAt:13.34 "
"NAME 'olcBkLloadStartTLS' "
"DESC 'Whether StartTLS should be attempted on the connection' "
"EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL
},
#endif /* BALANCER_MODULE */
{ NULL, NULL, 0, 0, 0, ARG_IGNORED, NULL }
};
#ifdef BALANCER_MODULE
static ConfigCfAdd lload_cfadd;
static ConfigLDAPadd lload_backend_ldadd;
#ifdef SLAP_CONFIG_DELETE
static ConfigLDAPdel lload_backend_lddel;
#endif /* SLAP_CONFIG_DELETE */
static ConfigOCs lloadocs[] = {
{ "( OLcfgBkOc:13.1 "
"NAME 'olcBkLloadConfig' "
"DESC 'Lload backend configuration' "
"SUP olcBackendConfig "
"MUST ( olcBkLloadBindconf "
"$ olcBkLloadIOThreads "
"$ olcBkLloadListen "
"$ olcBkLloadSockbufMaxClient "
"$ olcBkLloadSockbufMaxUpstream "
"$ olcBkLloadMaxPDUPerCycle "
"$ olcBkLloadIOTimeout ) "
"MAY ( olcBkLloadFeature "
"$ olcBkLloadTcpBuffer "
"$ olcBkLloadTLSCACertificateFile "
"$ olcBkLloadTLSCACertificatePath "
"$ olcBkLloadTLSCertificateFile "
"$ olcBkLloadTLSCertificateKeyFile "
"$ olcBkLloadTLSCipherSuite "
"$ olcBkLloadTLSCRLCheck "
"$ olcBkLloadTLSRandFile "
"$ olcBkLloadVerifyClient "
"$ olcBkLloadTLSDHParamFile "
"$ olcBkLloadTLSECName "
"$ olcBkLloadTLSProtocolMin "
"$ olcBkLloadTLSCRLFile "
"$ olcBkLloadTLSShareSlapdCTX "
") )",
Cft_Backend, config_back_cf_table,
NULL,
lload_cfadd,
},
{ "( OLcfgBkOc:13.2 "
"NAME 'olcBkLloadBackendConfig' "
"DESC 'Lload backend server configuration' "
"SUP olcConfig STRUCTURAL "
"MUST ( cn "
"$ olcBkLloadBackendUri "
"$ olcBkLloadNumconns "
"$ olcBkLloadBindconns "
"$ olcBkLloadRetry "
"$ olcBkLloadMaxPendingOps "
"$ olcBkLloadMaxPendingConns ) "
"MAY ( olcBkLloadStartTLS "
") )",
Cft_Misc, config_back_cf_table,
lload_backend_ldadd,
NULL,
#ifdef SLAP_CONFIG_DELETE
lload_backend_lddel,
#endif /* SLAP_CONFIG_DELETE */
},
{ NULL, 0, NULL }
};
#endif /* BALANCER_MODULE */
static int
config_generic( ConfigArgs *c )
{
enum lcf_daemon flag = 0;
int rc = LDAP_SUCCESS;
if ( c->op == SLAP_CONFIG_EMIT ) {
switch ( c->type ) {
case CFG_IOTHREADS:
c->value_uint = lload_daemon_threads;
break;
case CFG_LISTEN_URI: {
LloadListener **ll = lloadd_get_listeners();
struct berval bv = BER_BVNULL;
for ( ; ll && *ll; ll++ ) {
/* The same url could have spawned several consecutive
* listeners */
if ( !BER_BVISNULL( &bv ) &&
!ber_bvcmp( &bv, &(*ll)->sl_url ) ) {
continue;
}
ber_dupbv( &bv, &(*ll)->sl_url );
ber_bvarray_add( &c->rvalue_vals, &bv );
}
} break;
case CFG_MAXBUF_CLIENT:
c->value_uint = sockbuf_max_incoming_client;
break;
case CFG_MAXBUF_UPSTREAM:
c->value_uint = sockbuf_max_incoming_upstream;
break;
case CFG_RESCOUNT:
c->value_uint = lload_conn_max_pdus_per_cycle;
break;
case CFG_IOTIMEOUT:
c->value_uint = 1000 * lload_write_timeout->tv_sec +
lload_write_timeout->tv_usec / 1000;
break;
case CFG_CLIENT_PENDING:
c->value_uint = lload_client_max_pending;
break;
default:
rc = 1;
break;
}
return rc;
} else if ( c->op == LDAP_MOD_DELETE ) {
/* We only need to worry about deletions to multi-value or MAY
* attributes that belong to the lloadd module - we don't have any at
* the moment */
return rc;
}
lload_change.type = LLOAD_CHANGE_MODIFY;
lload_change.object = LLOAD_DAEMON;
switch ( c->type ) {
case CFG_CONCUR:
ldap_pvt_thread_set_concurrency( c->value_uint );
break;
case CFG_LISTEN:
if ( lloadd_inited ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"listen directive can only be specified once" );
ch_free( c->value_string );
return 1;
}
if ( lloadd_listeners_init( c->value_string ) ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"could not open one of the listener sockets: %s",
c->value_string );
ch_free( c->value_string );
return 1;
}
ch_free( c->value_string );
break;
case CFG_LISTEN_URI: {
LDAPURLDesc *lud;
LloadListener *l;
if ( ldap_url_parse_ext(
c->line, &lud, LDAP_PVT_URL_PARSE_DEF_PORT ) ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"string %s could not be parsed as an LDAP URL",
c->line );
goto fail;
}
/* A sanity check, although it will not catch everything */
if ( ( l = lload_config_check_my_url( c->line, lud ) ) ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"Load Balancer already configured to listen on %s "
"(while adding %s)",
l->sl_url.bv_val, c->line );
goto fail;
}
if ( !lloadd_inited ) {
if ( lload_open_new_listener( c->line, lud ) ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"could not open a listener for %s", c->line );
goto fail;
}
} else {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"listener changes will not take effect until restart: "
"%s",
c->line );
Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
}
} break;
case CFG_THREADS:
if ( c->value_uint < 2 ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"threads=%d smaller than minimum value 2",
c->value_uint );
goto fail;
} else if ( c->value_uint > 2 * SLAP_MAX_WORKER_THREADS ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"warning, threads=%d larger than twice the default "
"(2*%d=%d); YMMV",
c->value_uint, SLAP_MAX_WORKER_THREADS,
2 * SLAP_MAX_WORKER_THREADS );
Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
}
if ( slapMode & SLAP_SERVER_MODE )
ldap_pvt_thread_pool_maxthreads(
&connection_pool, c->value_uint );
connection_pool_max = c->value_uint; /* save for reference */
break;
case CFG_THREADQS:
if ( c->value_uint < 1 ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"threadqueues=%d smaller than minimum value 1",
c->value_uint );
goto fail;
}
if ( slapMode & SLAP_SERVER_MODE )
ldap_pvt_thread_pool_queues( &connection_pool, c->value_uint );
connection_pool_queues = c->value_uint; /* save for reference */
break;
case CFG_IOTHREADS: {
int mask = 0;
/* use a power of two */
while ( c->value_uint > 1 ) {
c->value_uint >>= 1;
mask <<= 1;
mask |= 1;
}
if ( !lloadd_inited ) {
lload_daemon_mask = mask;
lload_daemon_threads = mask + 1;
flag = LLOAD_DAEMON_MOD_THREADS;
} else {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"io thread changes will not take effect until "
"restart" );
Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
}
} break;
case CFG_LOGFILE: {
if ( logfileName ) ch_free( logfileName );
logfileName = c->value_string;
logfile = fopen( logfileName, "w" );
if ( logfile ) lutil_debug_file( logfile );
} break;
case CFG_RESCOUNT:
lload_conn_max_pdus_per_cycle = c->value_uint;
break;
case CFG_IOTIMEOUT:
if ( c->value_uint > 0 ) {
timeout_write_tv.tv_sec = c->value_uint / 1000;
timeout_write_tv.tv_usec = 1000 * ( c->value_uint % 1000 );
lload_write_timeout = &timeout_write_tv;
} else {
lload_write_timeout = NULL;
}
break;
case CFG_MAXBUF_CLIENT:
sockbuf_max_incoming_client = c->value_uint;
break;
case CFG_MAXBUF_UPSTREAM:
sockbuf_max_incoming_upstream = c->value_uint;
break;
case CFG_CLIENT_PENDING:
lload_client_max_pending = c->value_uint;
break;
default:
Debug( LDAP_DEBUG_ANY, "%s: unknown CFG_TYPE %d\n",
c->log, c->type );
return 1;
}
lload_change.flags.daemon |= flag;
return 0;
fail:
if ( lload_change.type == LLOAD_CHANGE_ADD ) {
/* Abort the ADD */
lload_change.type = LLOAD_CHANGE_DEL;
}
Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
return 1;
}
static int
lload_backend_finish( ConfigArgs *ca )
{
LloadBackend *b = ca->ca_private;
if ( ca->reply.err != LDAP_SUCCESS ) {
/* Not reached since cleanup is only called on success */
goto fail;
}
if ( b->b_numconns <= 0 || b->b_numbindconns <= 0 ) {
Debug( LDAP_DEBUG_ANY, "lload_backend_finish: "
"invalid connection pool configuration\n" );
goto fail;
}
if ( b->b_retry_timeout < 0 ) {
Debug( LDAP_DEBUG_ANY, "lload_backend_finish: "
"invalid retry timeout configuration\n" );
goto fail;
}
b->b_retry_tv.tv_sec = b->b_retry_timeout / 1000;
b->b_retry_tv.tv_usec = ( b->b_retry_timeout % 1000 ) * 1000;
/* daemon_base is only allocated after initial configuration happens, those
* events are allocated on startup, we only deal with online Adds */
if ( !b->b_retry_event && daemon_base ) {
struct event *event;
assert( CONFIG_ONLINE_ADD( ca ) );
event = evtimer_new( daemon_base, backend_connect, b );
if ( !event ) {
Debug( LDAP_DEBUG_ANY, "lload_backend_finish: "
"failed to allocate retry event\n" );
goto fail;
}
b->b_retry_event = event;
}
return LDAP_SUCCESS;
fail:
if ( lload_change.type == LLOAD_CHANGE_ADD ) {
/* Abort the ADD */
lload_change.type = LLOAD_CHANGE_DEL;
}
lload_backend_destroy( b );
return -1;
}
static LloadBackend *
backend_alloc( void )
{
LloadBackend *b;
b = ch_calloc( 1, sizeof(LloadBackend) );
LDAP_CIRCLEQ_INIT( &b->b_conns );
LDAP_CIRCLEQ_INIT( &b->b_bindconns );
LDAP_CIRCLEQ_INIT( &b->b_preparing );
b->b_numconns = 1;
b->b_numbindconns = 1;
b->b_retry_timeout = 5000;
ldap_pvt_thread_mutex_init( &b->b_mutex );
LDAP_CIRCLEQ_INSERT_TAIL( &backend, b, b_next );
return b;
}
static int
backend_config_url( LloadBackend *b, struct berval *uri )
{
LDAPURLDesc *lud = NULL;
char *host = NULL;
int rc, proto, tls = b->b_tls_conf;
/* Effect no changes until we've checked everything */
rc = ldap_url_parse_ext( uri->bv_val, &lud, LDAP_PVT_URL_PARSE_DEF_PORT );
if ( rc != LDAP_URL_SUCCESS ) {
Debug( LDAP_DEBUG_ANY, "backend_config_url: "
"listen URL \"%s\" parse error=%d\n",
uri->bv_val, rc );
return -1;
}
if ( ldap_pvt_url_scheme2tls( lud->lud_scheme ) ) {
#ifdef HAVE_TLS
/* Specifying ldaps:// overrides starttls= settings */
tls = LLOAD_LDAPS;
#else /* ! HAVE_TLS */
Debug( LDAP_DEBUG_ANY, "backend_config_url: "
"TLS not supported (%s)\n",
uri->bv_val );
rc = -1;
goto done;
#endif /* ! HAVE_TLS */
}
proto = ldap_pvt_url_scheme2proto( lud->lud_scheme );
if ( proto == LDAP_PROTO_IPC ) {
#ifdef LDAP_PF_LOCAL
if ( lud->lud_host == NULL || lud->lud_host[0] == '\0' ) {
host = LDAPI_SOCK;
}
#else /* ! LDAP_PF_LOCAL */
Debug( LDAP_DEBUG_ANY, "backend_config_url: "
"URL scheme not supported: %s",
url );
rc = -1;
goto done;
#endif /* ! LDAP_PF_LOCAL */
} else {
if ( lud->lud_host == NULL || lud->lud_host[0] == '\0' ) {
Debug( LDAP_DEBUG_ANY, "backend_config_url: "
"backend url missing hostname: '%s'\n",
uri->bv_val );
rc = -1;
goto done;
}
}
if ( !host ) {
host = lud->lud_host;
}
if ( b->b_host ) {
ch_free( b->b_host );
}
b->b_proto = proto;
b->b_tls = tls;
b->b_port = lud->lud_port;
b->b_host = ch_strdup( host );
done:
ldap_free_urldesc( lud );
return rc;
}
static int
config_backend( ConfigArgs *c )
{
LloadBackend *b;
int i, rc = 0;
b = backend_alloc();
for ( i = 1; i < c->argc; i++ ) {
if ( lload_backend_parse( c->argv[i], b ) ) {
Debug( LDAP_DEBUG_ANY, "config_backend: "
"error parsing backend configuration item '%s'\n",
c->argv[i] );
return -1;
}
}
if ( BER_BVISNULL( &b->b_uri ) ) {
Debug( LDAP_DEBUG_ANY, "config_backend: "
"backend address not specified\n" );
rc = -1;
goto done;
}
if ( backend_config_url( b, &b->b_uri ) ) {
rc = -1;
goto done;
}
c->ca_private = b;
rc = lload_backend_finish( c );
done:
if ( rc ) {
ch_free( b );
}
return rc;
}
static int
config_bindconf( ConfigArgs *c )
{
int i;
if ( c->op == SLAP_CONFIG_EMIT ) {
struct berval bv;
lload_bindconf_unparse( &bindconf, &bv );
for ( i = 0; isspace( (unsigned char)bv.bv_val[i] ); i++ )
/* count spaces */;
if ( i ) {
bv.bv_len -= i;
AC_MEMCPY( bv.bv_val, &bv.bv_val[i], bv.bv_len + 1 );
}
value_add_one( &c->rvalue_vals, &bv );
ber_memfree( bv.bv_val );
return LDAP_SUCCESS;
} else if ( c->op == LDAP_MOD_DELETE ) {
/* It's a MUST single-valued attribute, noop for now */
lload_bindconf_free( &bindconf );
return LDAP_SUCCESS;
}
lload_change.type = LLOAD_CHANGE_MODIFY;
lload_change.object = LLOAD_DAEMON;
lload_change.flags.daemon |= LLOAD_DAEMON_MOD_BINDCONF;
for ( i = 1; i < c->argc; i++ ) {
if ( lload_bindconf_parse( c->argv[i], &bindconf ) ) {
Debug( LDAP_DEBUG_ANY, "config_bindconf: "
"error parsing backend configuration item '%s'\n",
c->argv[i] );
return -1;
}
}
if ( bindconf.sb_method == LDAP_AUTH_SASL ) {
#ifndef HAVE_CYRUS_SASL
Debug( LDAP_DEBUG_ANY, "config_bindconf: "
"no sasl support available\n" );
return -1;
#endif
}
if ( !BER_BVISNULL( &bindconf.sb_authzId ) ) {
ber_dupbv( &lloadd_identity, &bindconf.sb_authzId );
} else if ( !BER_BVISNULL( &bindconf.sb_authcId ) ) {
ber_dupbv( &lloadd_identity, &bindconf.sb_authcId );
} else if ( !BER_BVISNULL( &bindconf.sb_binddn ) ) {
char *ptr;
lloadd_identity.bv_len = STRLENOF("dn:") + bindconf.sb_binddn.bv_len;
lloadd_identity.bv_val = ch_malloc( lloadd_identity.bv_len + 1 );
ptr = lutil_strcopy( lloadd_identity.bv_val, "dn:" );
ptr = lutil_strncopy(
ptr, bindconf.sb_binddn.bv_val, bindconf.sb_binddn.bv_len );
*ptr = '\0';
}
if ( bindconf.sb_timeout_api ) {
timeout_api_tv.tv_sec = bindconf.sb_timeout_api;
lload_timeout_api = &timeout_api_tv;
if ( lload_timeout_event ) {
event_add( lload_timeout_event, lload_timeout_api );
}
} else {
lload_timeout_api = NULL;
if ( lload_timeout_event ) {
event_del( lload_timeout_event );
}
}
if ( bindconf.sb_timeout_net ) {
timeout_net_tv.tv_sec = bindconf.sb_timeout_net;
lload_timeout_net = &timeout_net_tv;
} else {
lload_timeout_net = NULL;
}
#ifdef HAVE_TLS
if ( bindconf.sb_tls_do_init ) {
lload_bindconf_tls_set( &bindconf, lload_tls_backend_ld );
}
#endif /* HAVE_TLS */
return 0;
}
static int
config_fname( ConfigArgs *c )
{
return 0;
}
/*
* [listener=<listener>] [{read|write}=]<size>
*/
#ifdef LDAP_TCP_BUFFER
static BerVarray tcp_buffer;
int tcp_buffer_num;
#define SLAP_TCP_RMEM ( 0x1U )
#define SLAP_TCP_WMEM ( 0x2U )
static int
tcp_buffer_parse(
struct berval *val,
int argc,
char **argv,
int *size,
int *rw,
LloadListener **l )
{
int i, rc = LDAP_SUCCESS;
LDAPURLDesc *lud = NULL;
char *ptr;
if ( val != NULL && argv == NULL ) {
char *s = val->bv_val;
argv = ldap_str2charray( s, " \t" );
if ( argv == NULL ) {
return LDAP_OTHER;
}
}
i = 0;
if ( strncasecmp( argv[i], "listener=", STRLENOF("listener=") ) == 0 ) {
char *url = argv[i] + STRLENOF("listener=");
if ( ldap_url_parse_ext( url, &lud, LDAP_PVT_URL_PARSE_DEF_PORT ) ) {
rc = LDAP_INVALID_SYNTAX;
goto done;
}
*l = lload_config_check_my_url( url, lud );
if ( *l == NULL ) {
rc = LDAP_NO_SUCH_ATTRIBUTE;
goto done;
}
i++;
}
ptr = argv[i];
if ( strncasecmp( ptr, "read=", STRLENOF("read=") ) == 0 ) {
*rw |= SLAP_TCP_RMEM;
ptr += STRLENOF("read=");
} else if ( strncasecmp( ptr, "write=", STRLENOF("write=") ) == 0 ) {
*rw |= SLAP_TCP_WMEM;
ptr += STRLENOF("write=");
} else {
*rw |= ( SLAP_TCP_RMEM | SLAP_TCP_WMEM );
}
/* accept any base */
if ( lutil_atoix( size, ptr, 0 ) ) {
rc = LDAP_INVALID_SYNTAX;
goto done;
}
done:;
if ( val != NULL && argv != NULL ) {
ldap_charray_free( argv );
}
if ( lud != NULL ) {
ldap_free_urldesc( lud );
}
return rc;
}
#ifdef BALANCER_MODULE
static int
tcp_buffer_delete_one( struct berval *val )
{
int rc = 0;
int size = -1, rw = 0;
LloadListener *l = NULL;
rc = tcp_buffer_parse( val, 0, NULL, &size, &rw, &l );
if ( rc != 0 ) {
return rc;
}
if ( l != NULL ) {
int i;
LloadListener **ll = lloadd_get_listeners();
for ( i = 0; ll[i] != NULL; i++ ) {
if ( ll[i] == l ) break;
}
if ( ll[i] == NULL ) {
return LDAP_NO_SUCH_ATTRIBUTE;
}
if ( rw & SLAP_TCP_RMEM ) l->sl_tcp_rmem = -1;
if ( rw & SLAP_TCP_WMEM ) l->sl_tcp_wmem = -1;
for ( i++; ll[i] != NULL && bvmatch( &l->sl_url, &ll[i]->sl_url );
i++ ) {
if ( rw & SLAP_TCP_RMEM ) ll[i]->sl_tcp_rmem = -1;
if ( rw & SLAP_TCP_WMEM ) ll[i]->sl_tcp_wmem = -1;
}
} else {
/* NOTE: this affects listeners without a specific setting,
* does not reset all listeners. If a listener without
* specific settings was assigned a buffer because of
* a global setting, it will not be reset. In any case,
* buffer changes will only take place at restart. */
if ( rw & SLAP_TCP_RMEM ) slapd_tcp_rmem = -1;
if ( rw & SLAP_TCP_WMEM ) slapd_tcp_wmem = -1;
}
return rc;
}
static int
tcp_buffer_delete( BerVarray vals )
{
int i;
for ( i = 0; !BER_BVISNULL( &vals[i] ); i++ ) {
tcp_buffer_delete_one( &vals[i] );
}
return 0;
}
#endif /* BALANCER_MODULE */
static int
tcp_buffer_unparse( int size, int rw, LloadListener *l, struct berval *val )
{
char buf[sizeof("2147483648")], *ptr;
/* unparse for later use */
val->bv_len = snprintf( buf, sizeof(buf), "%d", size );
if ( l != NULL ) {
val->bv_len += STRLENOF( "listener="
" " ) +
l->sl_url.bv_len;
}
if ( rw != ( SLAP_TCP_RMEM | SLAP_TCP_WMEM ) ) {
if ( rw & SLAP_TCP_RMEM ) {
val->bv_len += STRLENOF("read=");
} else if ( rw & SLAP_TCP_WMEM ) {
val->bv_len += STRLENOF("write=");
}
}
val->bv_val = SLAP_MALLOC( val->bv_len + 1 );
ptr = val->bv_val;
if ( l != NULL ) {
ptr = lutil_strcopy( ptr, "listener=" );
ptr = lutil_strncopy( ptr, l->sl_url.bv_val, l->sl_url.bv_len );
*ptr++ = ' ';
}
if ( rw != ( SLAP_TCP_RMEM | SLAP_TCP_WMEM ) ) {
if ( rw & SLAP_TCP_RMEM ) {
ptr = lutil_strcopy( ptr, "read=" );
} else if ( rw & SLAP_TCP_WMEM ) {
ptr = lutil_strcopy( ptr, "write=" );
}
}
ptr = lutil_strcopy( ptr, buf );
*ptr = '\0';
assert( val->bv_val + val->bv_len == ptr );
return LDAP_SUCCESS;
}
static int
tcp_buffer_add_one( int argc, char **argv )
{
int rc = 0;
int size = -1, rw = 0;
LloadListener *l = NULL;
struct berval val;
/* parse */
rc = tcp_buffer_parse( NULL, argc, argv, &size, &rw, &l );
if ( rc != 0 ) {
return rc;
}
/* unparse for later use */
rc = tcp_buffer_unparse( size, rw, l, &val );
if ( rc != LDAP_SUCCESS ) {
return rc;
}
/* use parsed values */
if ( l != NULL ) {
int i;
LloadListener **ll = lloadd_get_listeners();
for ( i = 0; ll[i] != NULL; i++ ) {
if ( ll[i] == l ) break;
}
if ( ll[i] == NULL ) {
return LDAP_NO_SUCH_ATTRIBUTE;
}
/* buffer only applies to TCP listeners;
* we do not do any check here, and delegate them
* to setsockopt(2) */
if ( rw & SLAP_TCP_RMEM ) l->sl_tcp_rmem = size;
if ( rw & SLAP_TCP_WMEM ) l->sl_tcp_wmem = size;
for ( i++; ll[i] != NULL && bvmatch( &l->sl_url, &ll[i]->sl_url );
i++ ) {
if ( rw & SLAP_TCP_RMEM ) ll[i]->sl_tcp_rmem = size;
if ( rw & SLAP_TCP_WMEM ) ll[i]->sl_tcp_wmem = size;
}
} else {
/* NOTE: this affects listeners without a specific setting,
* does not set all listeners */
if ( rw & SLAP_TCP_RMEM ) slapd_tcp_rmem = size;
if ( rw & SLAP_TCP_WMEM ) slapd_tcp_wmem = size;
}
tcp_buffer = SLAP_REALLOC(
tcp_buffer, sizeof(struct berval) * ( tcp_buffer_num + 2 ) );
/* append */
tcp_buffer[tcp_buffer_num] = val;
tcp_buffer_num++;
BER_BVZERO( &tcp_buffer[tcp_buffer_num] );
return rc;
}
static int
config_tcp_buffer( ConfigArgs *c )
{
int rc = LDAP_SUCCESS;
#ifdef BALANCER_MODULE
if ( c->op == SLAP_CONFIG_EMIT ) {
if ( tcp_buffer == NULL || BER_BVISNULL( &tcp_buffer[0] ) ) {
return 1;
}
value_add( &c->rvalue_vals, tcp_buffer );
value_add( &c->rvalue_nvals, tcp_buffer );
return 0;
} else if ( c->op == LDAP_MOD_DELETE ) {
if ( !c->line ) {
tcp_buffer_delete( tcp_buffer );
ber_bvarray_free( tcp_buffer );
tcp_buffer = NULL;
tcp_buffer_num = 0;
} else {
int size = -1, rw = 0;
LloadListener *l = NULL;
struct berval val = BER_BVNULL;
int i;
if ( tcp_buffer_num == 0 ) {
return 1;
}
/* parse */
rc = tcp_buffer_parse(
NULL, c->argc - 1, &c->argv[1], &size, &rw, &l );
if ( rc != 0 ) {
return 1;
}
/* unparse for later use */
rc = tcp_buffer_unparse( size, rw, l, &val );
if ( rc != LDAP_SUCCESS ) {
return 1;
}
for ( i = 0; !BER_BVISNULL( &tcp_buffer[i] ); i++ ) {
if ( bvmatch( &tcp_buffer[i], &val ) ) {
break;
}
}
if ( BER_BVISNULL( &tcp_buffer[i] ) ) {
/* not found */
rc = 1;
goto done;
}
tcp_buffer_delete_one( &tcp_buffer[i] );
ber_memfree( tcp_buffer[i].bv_val );
for ( ; i < tcp_buffer_num; i++ ) {
tcp_buffer[i] = tcp_buffer[i + 1];
}
tcp_buffer_num--;
done:;
if ( !BER_BVISNULL( &val ) ) {
SLAP_FREE(val.bv_val);
}
}
return rc;
}
#endif /* BALANCER_MODULE */
rc = tcp_buffer_add_one( c->argc - 1, &c->argv[1] );
if ( rc ) {
snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> unable to add value #%d",
c->argv[0], tcp_buffer_num );
Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
return 1;
}
return 0;
}
#endif /* LDAP_TCP_BUFFER */
static int
config_restrict( ConfigArgs *c )
{
slap_mask_t restrictops = 0;
int i;
slap_verbmasks restrictable_ops[] = {
{ BER_BVC("bind"), SLAP_RESTRICT_OP_BIND },
{ BER_BVC("add"), SLAP_RESTRICT_OP_ADD },
{ BER_BVC("modify"), SLAP_RESTRICT_OP_MODIFY },
{ BER_BVC("rename"), SLAP_RESTRICT_OP_RENAME },
{ BER_BVC("modrdn"), 0 },
{ BER_BVC("delete"), SLAP_RESTRICT_OP_DELETE },
{ BER_BVC("search"), SLAP_RESTRICT_OP_SEARCH },
{ BER_BVC("compare"), SLAP_RESTRICT_OP_COMPARE },
{ BER_BVC("read"), SLAP_RESTRICT_OP_READS },
{ BER_BVC("write"), SLAP_RESTRICT_OP_WRITES },
{ BER_BVC("extended"), SLAP_RESTRICT_OP_EXTENDED },
{ BER_BVC("extended=" LDAP_EXOP_START_TLS), SLAP_RESTRICT_EXOP_START_TLS },
{ BER_BVC("extended=" LDAP_EXOP_MODIFY_PASSWD), SLAP_RESTRICT_EXOP_MODIFY_PASSWD },
{ BER_BVC("extended=" LDAP_EXOP_X_WHO_AM_I), SLAP_RESTRICT_EXOP_WHOAMI },
{ BER_BVC("extended=" LDAP_EXOP_X_CANCEL), SLAP_RESTRICT_EXOP_CANCEL },
{ BER_BVC("all"), SLAP_RESTRICT_OP_ALL },
{ BER_BVNULL, 0 }
};
i = verbs_to_mask( c->argc, c->argv, restrictable_ops, &restrictops );
if ( i ) {
snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> unknown operation",
c->argv[0] );
Debug( LDAP_DEBUG_ANY, "%s: %s %s\n",
c->log, c->cr_msg, c->argv[i] );
return 1;
}
if ( restrictops & SLAP_RESTRICT_OP_EXTENDED )
restrictops &= ~SLAP_RESTRICT_EXOP_MASK;
return 0;
}
static slap_verbmasks *loglevel_ops;
static int
loglevel_init( void )
{
slap_verbmasks lo[] = {
{ BER_BVC("Any"), (slap_mask_t)LDAP_DEBUG_ANY },
{ BER_BVC("Trace"), LDAP_DEBUG_TRACE },
{ BER_BVC("Packets"), LDAP_DEBUG_PACKETS },
{ BER_BVC("Args"), LDAP_DEBUG_ARGS },
{ BER_BVC("Conns"), LDAP_DEBUG_CONNS },
{ BER_BVC("BER"), LDAP_DEBUG_BER },
{ BER_BVC("Filter"), LDAP_DEBUG_FILTER },
{ BER_BVC("Config"), LDAP_DEBUG_CONFIG },
{ BER_BVC("ACL"), LDAP_DEBUG_ACL },
{ BER_BVC("Stats"), LDAP_DEBUG_STATS },
{ BER_BVC("Stats2"), LDAP_DEBUG_STATS2 },
{ BER_BVC("Shell"), LDAP_DEBUG_SHELL },
{ BER_BVC("Parse"), LDAP_DEBUG_PARSE },
{ BER_BVC("Sync"), LDAP_DEBUG_SYNC },
{ BER_BVC("None"), LDAP_DEBUG_NONE },
{ BER_BVNULL, 0 }
};
return slap_verbmasks_init( &loglevel_ops, lo );
}
static void
loglevel_destroy( void )
{
if ( loglevel_ops ) {
(void)slap_verbmasks_destroy( loglevel_ops );
}
loglevel_ops = NULL;
}
int
str2loglevel( const char *s, int *l )
{
int i;
if ( loglevel_ops == NULL ) {
loglevel_init();
}
i = verb_to_mask( s, loglevel_ops );
if ( BER_BVISNULL( &loglevel_ops[i].word ) ) {
return -1;
}
*l = loglevel_ops[i].mask;
return 0;
}
int
loglevel2bvarray( int l, BerVarray *bva )
{
if ( loglevel_ops == NULL ) {
loglevel_init();
}
if ( l == 0 ) {
struct berval bv = BER_BVC("0");
return value_add_one( bva, &bv );
}
return mask_to_verbs( loglevel_ops, l, bva );
}
int
loglevel_print( FILE *out )
{
int i;
if ( loglevel_ops == NULL ) {
loglevel_init();
}
fprintf( out, "Installed log subsystems:\n\n" );
for ( i = 0; !BER_BVISNULL( &loglevel_ops[i].word ); i++ ) {
unsigned mask = loglevel_ops[i].mask & 0xffffffffUL;
fprintf( out,
( mask == ( (slap_mask_t)-1 & 0xffffffffUL ) ?
"\t%-30s (-1, 0xffffffff)\n" :
"\t%-30s (%u, 0x%x)\n" ),
loglevel_ops[i].word.bv_val, mask, mask );
}
fprintf( out,
"\nNOTE: custom log subsystems may be later installed "
"by specific code\n\n" );
return 0;
}
static int config_syslog;
static int
config_loglevel( ConfigArgs *c )
{
int i;
if ( loglevel_ops == NULL ) {
loglevel_init();
}
if ( c->op == SLAP_CONFIG_EMIT ) {
/* Get default or commandline slapd setting */
if ( ldap_syslog && !config_syslog ) config_syslog = ldap_syslog;
return loglevel2bvarray( config_syslog, &c->rvalue_vals );
} else if ( c->op == LDAP_MOD_DELETE ) {
if ( !c->line ) {
config_syslog = 0;
} else {
i = verb_to_mask( c->line, loglevel_ops );
config_syslog &= ~loglevel_ops[i].mask;
}
if ( slapMode & SLAP_SERVER_MODE ) {
ldap_syslog = config_syslog;
}
return 0;
}
for ( i = 1; i < c->argc; i++ ) {
int level;
if ( isdigit( (unsigned char)c->argv[i][0] ) || c->argv[i][0] == '-' ) {
if ( lutil_atoix( &level, c->argv[i], 0 ) != 0 ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"<%s> unable to parse level",
c->argv[0] );
Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
c->log, c->cr_msg, c->argv[i] );
return 1;
}
} else {
if ( str2loglevel( c->argv[i], &level ) ) {
snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> unknown level",
c->argv[0] );
Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
c->log, c->cr_msg, c->argv[i] );
return 1;
}
}
/* Explicitly setting a zero clears all the levels */
if ( level )
config_syslog |= level;
else
config_syslog = 0;
}
if ( slapMode & SLAP_SERVER_MODE ) {
ldap_syslog = config_syslog;
}
return 0;
}
static int
config_include( ConfigArgs *c )
{
int savelineno = c->lineno;
int rc;
ConfigFile *cf;
ConfigFile *cfsave = cfn;
ConfigFile *cf2 = NULL;
/* Leftover from RE23. No dynamic config for include files */
if ( c->op == SLAP_CONFIG_EMIT || c->op == LDAP_MOD_DELETE ) return 1;
cf = ch_calloc( 1, sizeof(ConfigFile) );
if ( cfn->c_kids ) {
for ( cf2 = cfn->c_kids; cf2 && cf2->c_sibs; cf2 = cf2->c_sibs )
/* empty */;
cf2->c_sibs = cf;
} else {
cfn->c_kids = cf;
}
cfn = cf;
ber_str2bv( c->argv[1], 0, 1, &cf->c_file );
rc = lload_read_config_file(
c->argv[1], c->depth + 1, c, config_back_cf_table );
c->lineno = savelineno - 1;
cfn = cfsave;
if ( rc ) {
if ( cf2 )
cf2->c_sibs = NULL;
else
cfn->c_kids = NULL;
ch_free( cf->c_file.bv_val );
ch_free( cf );
} else {
c->ca_private = cf;
}
return rc;
}
static int
config_feature( ConfigArgs *c )
{
slap_verbmasks features[] = {
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
{ BER_BVC("vc"), LLOAD_FEATURE_VC },
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
{ BER_BVC("proxyauthz"), LLOAD_FEATURE_PROXYAUTHZ },
{ BER_BVC("read_pause"), LLOAD_FEATURE_PAUSE },
{ BER_BVNULL, 0 }
};
slap_mask_t mask = 0;
int i;
if ( c->op == SLAP_CONFIG_EMIT ) {
return mask_to_verbs( features, lload_features, &c->rvalue_vals );
}
lload_change.type = LLOAD_CHANGE_MODIFY;
lload_change.object = LLOAD_DAEMON;
lload_change.flags.daemon |= LLOAD_DAEMON_MOD_FEATURES;
if ( !lload_change.target ) {
lload_change.target = (void *)(uintptr_t)~lload_features;
}
if ( c->op == LDAP_MOD_DELETE ) {
if ( !c->line ) {
/* Last value has been deleted */
lload_features = 0;
} else {
i = verb_to_mask( c->line, features );
lload_features &= ~features[i].mask;
}
return 0;
}
i = verbs_to_mask( c->argc, c->argv, features, &mask );
if ( i ) {
Debug( LDAP_DEBUG_ANY, "%s: <%s> unknown feature %s\n", c->log,
c->argv[0], c->argv[i] );
return 1;
}
lload_features |= mask;
return 0;
}
#ifdef HAVE_TLS
static int
config_tls_cleanup( ConfigArgs *c )
{
int rc = 0;
if ( lload_tls_ld ) {
int opt = 1;
ldap_pvt_tls_ctx_free( lload_tls_ctx );
lload_tls_ctx = NULL;
/* Force new ctx to be created */
rc = ldap_pvt_tls_set_option(
lload_tls_ld, LDAP_OPT_X_TLS_NEWCTX, &opt );
if ( rc == 0 ) {
/* The ctx's refcount is bumped up here */
ldap_pvt_tls_get_option(
lload_tls_ld, LDAP_OPT_X_TLS_CTX, &lload_tls_ctx );
} else {
if ( rc == LDAP_NOT_SUPPORTED )
rc = LDAP_UNWILLING_TO_PERFORM;
else
rc = LDAP_OTHER;
}
}
return rc;
}
static int
config_tls_option( ConfigArgs *c )
{
int flag;
int berval = 0;
LDAP *ld = lload_tls_ld;
switch ( c->type ) {
case CFG_TLS_RAND:
flag = LDAP_OPT_X_TLS_RANDOM_FILE;
ld = NULL;
break;
case CFG_TLS_CIPHER:
flag = LDAP_OPT_X_TLS_CIPHER_SUITE;
break;
case CFG_TLS_CERT_FILE:
flag = LDAP_OPT_X_TLS_CERTFILE;
break;
case CFG_TLS_CERT_KEY:
flag = LDAP_OPT_X_TLS_KEYFILE;
break;
case CFG_TLS_CA_PATH:
flag = LDAP_OPT_X_TLS_CACERTDIR;
break;
case CFG_TLS_CA_FILE:
flag = LDAP_OPT_X_TLS_CACERTFILE;
break;
case CFG_TLS_DH_FILE:
flag = LDAP_OPT_X_TLS_DHFILE;
break;
case CFG_TLS_ECNAME:
flag = LDAP_OPT_X_TLS_ECNAME;
break;
#ifdef HAVE_GNUTLS
case CFG_TLS_CRL_FILE:
flag = LDAP_OPT_X_TLS_CRLFILE;
break;
#endif
case CFG_TLS_CACERT:
flag = LDAP_OPT_X_TLS_CACERT;
berval = 1;
break;
case CFG_TLS_CERT:
flag = LDAP_OPT_X_TLS_CERT;
berval = 1;
break;
case CFG_TLS_KEY:
flag = LDAP_OPT_X_TLS_KEY;
berval = 1;
break;
default:
Debug( LDAP_DEBUG_ANY, "%s: "
"unknown tls_option <0x%x>\n",
c->log, c->type );
return 1;
}
if ( c->op == SLAP_CONFIG_EMIT ) {
return ldap_pvt_tls_get_option( ld, flag,
berval ? (void *)&c->value_bv : (void *)&c->value_string );
}
lload_change.type = LLOAD_CHANGE_MODIFY;
lload_change.object = LLOAD_DAEMON;
lload_change.flags.daemon |= LLOAD_DAEMON_MOD_TLS;
config_push_cleanup( c, config_tls_cleanup );
if ( c->op == LDAP_MOD_DELETE ) {
return ldap_pvt_tls_set_option( ld, flag, NULL );
}
if ( !berval ) ch_free( c->value_string );
return ldap_pvt_tls_set_option(
ld, flag, berval ? (void *)&c->value_bv : (void *)c->argv[1] );
}
/* FIXME: this ought to be provided by libldap */
static int
config_tls_config( ConfigArgs *c )
{
int i, flag;
switch ( c->type ) {
case CFG_TLS_CRLCHECK:
flag = LDAP_OPT_X_TLS_CRLCHECK;
break;
case CFG_TLS_VERIFY:
flag = LDAP_OPT_X_TLS_REQUIRE_CERT;
break;
case CFG_TLS_PROTOCOL_MIN:
flag = LDAP_OPT_X_TLS_PROTOCOL_MIN;
break;
default:
Debug( LDAP_DEBUG_ANY, "%s: "
"unknown tls_option <0x%x>\n",
c->log, c->type );
return 1;
}
if ( c->op == SLAP_CONFIG_EMIT ) {
return lload_tls_get_config( lload_tls_ld, flag, &c->value_string );
}
lload_change.type = LLOAD_CHANGE_MODIFY;
lload_change.object = LLOAD_DAEMON;
lload_change.flags.daemon |= LLOAD_DAEMON_MOD_TLS;
config_push_cleanup( c, config_tls_cleanup );
if ( c->op == LDAP_MOD_DELETE ) {
int i = 0;
return ldap_pvt_tls_set_option( lload_tls_ld, flag, &i );
}
ch_free( c->value_string );
if ( isdigit( (unsigned char)c->argv[1][0] ) &&
c->type != CFG_TLS_PROTOCOL_MIN ) {
if ( lutil_atoi( &i, c->argv[1] ) != 0 ) {
Debug( LDAP_DEBUG_ANY, "%s: "
"unable to parse %s \"%s\"\n",
c->log, c->argv[0], c->argv[1] );
return 1;
}
return ldap_pvt_tls_set_option( lload_tls_ld, flag, &i );
} else {
return ldap_pvt_tls_config( lload_tls_ld, flag, c->argv[1] );
}
}
#endif
#ifdef BALANCER_MODULE
static int
config_share_tls_ctx( ConfigArgs *c )
{
int rc = LDAP_SUCCESS;
if ( c->op == SLAP_CONFIG_EMIT ) {
c->value_int = lload_use_slap_tls_ctx;
return rc;
}
lload_change.type = LLOAD_CHANGE_MODIFY;
lload_change.object = LLOAD_DAEMON;
lload_change.flags.daemon |= LLOAD_DAEMON_MOD_TLS;
if ( c->op == LDAP_MOD_DELETE ) {
lload_use_slap_tls_ctx = 0;
return rc;
}
lload_use_slap_tls_ctx = c->value_int;
return rc;
}
#endif /* BALANCER_MODULE */
void
lload_init_config_argv( ConfigArgs *c )
{
c->argv = ch_calloc( ARGS_STEP + 1, sizeof(*c->argv) );
c->argv_size = ARGS_STEP + 1;
}
ConfigTable *
lload_config_find_keyword( ConfigTable *Conf, ConfigArgs *c )
{
int i;
for ( i = 0; Conf[i].name; i++ )
if ( ( Conf[i].length &&
( !strncasecmp(
c->argv[0], Conf[i].name, Conf[i].length ) ) ) ||
( !strcasecmp( c->argv[0], Conf[i].name ) ) )
break;
if ( !Conf[i].name ) return NULL;
if ( (Conf[i].arg_type & ARGS_TYPES) == ARG_BINARY ) {
size_t decode_len = LUTIL_BASE64_DECODE_LEN( c->linelen );
ch_free( c->tline );
c->tline = ch_malloc( decode_len + 1 );
c->linelen = lutil_b64_pton( c->line, c->tline, decode_len );
if ( c->linelen < 0 ) {
ch_free( c->tline );
c->tline = NULL;
return NULL;
}
c->line = c->tline;
}
return Conf + i;
}
int
lload_config_check_vals( ConfigTable *Conf, ConfigArgs *c, int check_only )
{
int arg_user, arg_type, arg_syn, iarg;
unsigned uiarg;
long larg;
unsigned long ularg;
ber_len_t barg;
if ( Conf->arg_type == ARG_IGNORED ) {
Debug( LDAP_DEBUG_CONFIG, "%s: keyword <%s> ignored\n",
c->log, Conf->name );
return 0;
}
arg_type = Conf->arg_type & ARGS_TYPES;
arg_user = Conf->arg_type & ARGS_USERLAND;
arg_syn = Conf->arg_type & ARGS_SYNTAX;
if ( Conf->min_args && ( c->argc < Conf->min_args ) ) {
snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> missing <%s> argument",
c->argv[0], Conf->what ? Conf->what : "" );
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: keyword %s\n",
c->log, c->cr_msg );
return ARG_BAD_CONF;
}
if ( Conf->max_args && ( c->argc > Conf->max_args ) ) {
char *ignored = " ignored";
snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> extra cruft after <%s>",
c->argv[0], Conf->what );
ignored = "";
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s%s\n",
c->log, c->cr_msg, ignored );
return ARG_BAD_CONF;
}
if ( (arg_syn & ARG_PAREN) && *c->argv[1] != '(' /*')'*/ ) {
snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> old format not supported",
c->argv[0] );
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
c->log, c->cr_msg );
return ARG_BAD_CONF;
}
if ( arg_type && !Conf->arg_item && !(arg_syn & ARG_OFFSET) ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"<%s> invalid config_table, arg_item is NULL",
c->argv[0] );
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
c->log, c->cr_msg );
return ARG_BAD_CONF;
}
c->type = arg_user;
memset( &c->values, 0, sizeof(c->values) );
if ( arg_type == ARG_STRING ) {
assert( c->argc == 2 );
if ( !check_only ) c->value_string = ch_strdup( c->argv[1] );
} else if ( arg_type == ARG_BERVAL ) {
assert( c->argc == 2 );
if ( !check_only ) ber_str2bv( c->argv[1], 0, 1, &c->value_bv );
} else if ( arg_type == ARG_BINARY ) {
assert( c->argc == 2 );
if ( !check_only ) {
c->value_bv.bv_len = c->linelen;
c->value_bv.bv_val = ch_malloc( c->linelen );
AC_MEMCPY( c->value_bv.bv_val, c->line, c->linelen );
}
} else { /* all numeric */
int j;
iarg = 0;
larg = 0;
barg = 0;
switch ( arg_type ) {
case ARG_INT:
assert( c->argc == 2 );
if ( lutil_atoix( &iarg, c->argv[1], 0 ) != 0 ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"<%s> unable to parse \"%s\" as int",
c->argv[0], c->argv[1] );
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
c->log, c->cr_msg );
return ARG_BAD_CONF;
}
break;
case ARG_UINT:
assert( c->argc == 2 );
if ( lutil_atoux( &uiarg, c->argv[1], 0 ) != 0 ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"<%s> unable to parse \"%s\" as unsigned int",
c->argv[0], c->argv[1] );
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
c->log, c->cr_msg );
return ARG_BAD_CONF;
}
break;
case ARG_LONG:
assert( c->argc == 2 );
if ( lutil_atolx( &larg, c->argv[1], 0 ) != 0 ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"<%s> unable to parse \"%s\" as long",
c->argv[0], c->argv[1] );
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
c->log, c->cr_msg );
return ARG_BAD_CONF;
}
break;
case ARG_ULONG:
assert( c->argc == 2 );
if ( LUTIL_ATOULX( &ularg, c->argv[1], 0 ) != 0 ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"<%s> unable to parse \"%s\" as unsigned long",
c->argv[0], c->argv[1] );
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
c->log, c->cr_msg );
return ARG_BAD_CONF;
}
break;
case ARG_BER_LEN_T: {
unsigned long l;
assert( c->argc == 2 );
if ( lutil_atoulx( &l, c->argv[1], 0 ) != 0 ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"<%s> unable to parse \"%s\" as ber_len_t",
c->argv[0], c->argv[1] );
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
c->log, c->cr_msg );
return ARG_BAD_CONF;
}
barg = (ber_len_t)l;
} break;
case ARG_ON_OFF:
/* note: this is an explicit exception
* to the "need exactly 2 args" rule */
if ( c->argc == 1 ) {
iarg = 1;
} else if ( !strcasecmp( c->argv[1], "on" ) ||
!strcasecmp( c->argv[1], "true" ) ||
!strcasecmp( c->argv[1], "yes" ) ) {
iarg = 1;
} else if ( !strcasecmp( c->argv[1], "off" ) ||
!strcasecmp( c->argv[1], "false" ) ||
!strcasecmp( c->argv[1], "no" ) ) {
iarg = 0;
} else {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"<%s> invalid value",
c->argv[0] );
Debug( LDAP_DEBUG_ANY|LDAP_DEBUG_NONE, "%s: %s\n",
c->log, c->cr_msg );
return ARG_BAD_CONF;
}
break;
}
j = (arg_type & ARG_NONZERO) ? 1 : 0;
if ( iarg < j && larg < j && barg < (unsigned)j ) {
larg = larg ? larg : ( barg ? (long)barg : iarg );
snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> invalid value",
c->argv[0] );
Debug( LDAP_DEBUG_ANY|LDAP_DEBUG_NONE, "%s: %s\n",
c->log, c->cr_msg );
return ARG_BAD_CONF;
}
switch ( arg_type ) {
case ARG_ON_OFF:
case ARG_INT:
c->value_int = iarg;
break;
case ARG_UINT:
c->value_uint = uiarg;
break;
case ARG_LONG:
c->value_long = larg;
break;
case ARG_ULONG:
c->value_ulong = ularg;
break;
case ARG_BER_LEN_T:
c->value_ber_t = barg;
break;
}
}
return 0;
}
int
lload_config_set_vals( ConfigTable *Conf, ConfigArgs *c )
{
int rc, arg_type;
void *ptr = NULL;
arg_type = Conf->arg_type;
if ( arg_type & ARG_MAGIC ) {
c->cr_msg[0] = '\0';
rc = ( *( (ConfigDriver *)Conf->arg_item ) )( c );
if ( rc ) {
if ( !c->cr_msg[0] ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"<%s> handler exited with %d",
c->argv[0], rc );
Debug( LDAP_DEBUG_CONFIG, "%s: %s!\n", c->log, c->cr_msg );
}
return ARG_BAD_CONF;
}
return 0;
}
if ( arg_type & ARG_OFFSET ) {
{
snprintf( c->cr_msg, sizeof(c->cr_msg),
"<%s> offset is missing base pointer",
c->argv[0] );
Debug( LDAP_DEBUG_CONFIG, "%s: %s!\n", c->log, c->cr_msg );
return ARG_BAD_CONF;
}
ptr = (void *)( (char *)ptr + (long)Conf->arg_item );
} else if ( arg_type & ARGS_TYPES ) {
ptr = Conf->arg_item;
}
if ( arg_type & ARGS_TYPES ) switch ( arg_type & ARGS_TYPES ) {
case ARG_ON_OFF:
case ARG_INT:
*(int *)ptr = c->value_int;
break;
case ARG_UINT:
*(unsigned *)ptr = c->value_uint;
break;
case ARG_LONG:
*(long *)ptr = c->value_long;
break;
case ARG_ULONG:
*(size_t *)ptr = c->value_ulong;
break;
case ARG_BER_LEN_T:
*(ber_len_t *)ptr = c->value_ber_t;
break;
case ARG_STRING: {
char *cc = *(char **)ptr;
if ( cc ) {
if ( (arg_type & ARG_UNIQUE) &&
c->op == SLAP_CONFIG_ADD ) {
Debug( LDAP_DEBUG_CONFIG, "%s: already set %s!\n",
c->log, Conf->name );
return ARG_BAD_CONF;
}
ch_free( cc );
}
*(char **)ptr = c->value_string;
break;
}
case ARG_BERVAL:
case ARG_BINARY:
*(struct berval *)ptr = c->value_bv;
break;
}
return 0;
}
int
lload_config_add_vals( ConfigTable *Conf, ConfigArgs *c )
{
int rc, arg_type;
arg_type = Conf->arg_type;
if ( arg_type == ARG_IGNORED ) {
Debug( LDAP_DEBUG_CONFIG, "%s: keyword <%s> ignored\n",
c->log, Conf->name );
return 0;
}
rc = lload_config_check_vals( Conf, c, 0 );
if ( rc ) return rc;
return lload_config_set_vals( Conf, c );
}
int
lload_read_config_file(
const char *fname,
int depth,
ConfigArgs *cf,
ConfigTable *cft )
{
FILE *fp;
ConfigTable *ct;
ConfigArgs *c;
int rc;
struct stat s;
c = ch_calloc( 1, sizeof(ConfigArgs) );
if ( c == NULL ) {
return 1;
}
if ( depth ) {
memcpy( c, cf, sizeof(ConfigArgs) );
} else {
c->depth = depth; /* XXX */
}
c->valx = -1;
c->fname = fname;
lload_init_config_argv( c );
if ( stat( fname, &s ) != 0 ) {
char ebuf[128];
int saved_errno = errno;
ldap_syslog = 1;
Debug( LDAP_DEBUG_ANY, "could not stat config file \"%s\": %s (%d)\n",
fname, AC_STRERROR_R( saved_errno, ebuf, sizeof(ebuf) ),
saved_errno );
ch_free( c->argv );
ch_free( c );
return 1;
}
if ( !S_ISREG(s.st_mode) ) {
ldap_syslog = 1;
Debug( LDAP_DEBUG_ANY, "regular file expected, got \"%s\"\n", fname );
ch_free( c->argv );
ch_free( c );
return 1;
}
fp = fopen( fname, "r" );
if ( fp == NULL ) {
char ebuf[128];
int saved_errno = errno;
ldap_syslog = 1;
Debug( LDAP_DEBUG_ANY, "could not open config file \"%s\": %s (%d)\n",
fname, AC_STRERROR_R( saved_errno, ebuf, sizeof(ebuf) ),
saved_errno );
ch_free( c->argv );
ch_free( c );
return 1;
}
Debug( LDAP_DEBUG_CONFIG, "reading config file %s\n", fname );
fp_getline_init( c );
c->tline = NULL;
while ( fp_getline( fp, c ) ) {
/* skip comments and blank lines */
if ( c->line[0] == '#' || c->line[0] == '\0' ) {
continue;
}
snprintf( c->log, sizeof(c->log), "%s: line %d",
c->fname, c->lineno );
c->argc = 0;
ch_free( c->tline );
if ( lload_config_fp_parse_line( c ) ) {
rc = 1;
goto done;
}
if ( c->argc < 1 ) {
Debug( LDAP_DEBUG_ANY, "%s: bad config line\n", c->log );
rc = 1;
goto done;
}
c->op = SLAP_CONFIG_ADD;
ct = lload_config_find_keyword( cft, c );
if ( ct ) {
c->table = Cft_Global;
rc = lload_config_add_vals( ct, c );
if ( !rc ) continue;
if ( rc & ARGS_USERLAND ) {
/* XXX a usertype would be opaque here */
Debug( LDAP_DEBUG_CONFIG, "%s: unknown user type <%s>\n",
c->log, c->argv[0] );
rc = 1;
goto done;
} else if ( rc == ARG_BAD_CONF ) {
rc = 1;
goto done;
}
} else {
Debug( LDAP_DEBUG_ANY, "%s: unknown directive "
"<%s> outside backend info and database definitions\n",
c->log, *c->argv );
rc = 1;
goto done;
}
}
rc = 0;
done:
ch_free( c->tline );
fclose( fp );
ch_free( c->argv );
ch_free( c );
return rc;
}
int
lload_read_config( const char *fname, const char *dir )
{
if ( !fname ) fname = LLOADD_DEFAULT_CONFIGFILE;
cfn = ch_calloc( 1, sizeof(ConfigFile) );
return lload_read_config_file( fname, 0, NULL, config_back_cf_table );
}
/* restrictops, allows, disallows, requires, loglevel */
int
bverb_to_mask( struct berval *bword, slap_verbmasks *v )
{
int i;
for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
if ( !ber_bvstrcasecmp( bword, &v[i].word ) ) break;
}
return i;
}
int
verb_to_mask( const char *word, slap_verbmasks *v )
{
struct berval bword;
ber_str2bv( word, 0, 0, &bword );
return bverb_to_mask( &bword, v );
}
int
verbs_to_mask( int argc, char *argv[], slap_verbmasks *v, slap_mask_t *m )
{
int i, j;
for ( i = 1; i < argc; i++ ) {
j = verb_to_mask( argv[i], v );
if ( BER_BVISNULL( &v[j].word ) ) return i;
while ( !v[j].mask )
j--;
*m |= v[j].mask;
}
return 0;
}
/* Mask keywords that represent multiple bits should occur before single
* bit keywords in the verbmasks array.
*/
int
mask_to_verbs( slap_verbmasks *v, slap_mask_t m, BerVarray *bva )
{
int i, rc = 1;
if ( m ) {
for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
if ( !v[i].mask ) continue;
if ( (m & v[i].mask) == v[i].mask ) {
value_add_one( bva, &v[i].word );
rc = 0;
m ^= v[i].mask;
if ( !m ) break;
}
}
}
return rc;
}
int
slap_verbmasks_init( slap_verbmasks **vp, slap_verbmasks *v )
{
int i;
assert( *vp == NULL );
for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) /* EMPTY */;
*vp = ch_calloc( i + 1, sizeof(slap_verbmasks) );
for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
ber_dupbv( &(*vp)[i].word, &v[i].word );
*( (slap_mask_t *)&(*vp)[i].mask ) = v[i].mask;
}
BER_BVZERO( &(*vp)[i].word );
return 0;
}
int
slap_verbmasks_destroy( slap_verbmasks *v )
{
int i;
assert( v != NULL );
for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
ch_free( v[i].word.bv_val );
}
ch_free( v );
return 0;
}
#ifndef BALANCER_MODULE
int
config_push_cleanup( ConfigArgs *ca, ConfigDriver *cleanup )
{
/* Stub, cleanups only run in online config */
return 0;
}
#endif /* !BALANCER_MODULE */
static slap_verbmasks tlskey[] = {
{ BER_BVC("no"), LLOAD_CLEARTEXT },
{ BER_BVC("yes"), LLOAD_STARTTLS_OPTIONAL },
{ BER_BVC("critical"), LLOAD_STARTTLS },
{ BER_BVNULL, 0 }
};
static slap_verbmasks crlkeys[] = {
{ BER_BVC("none"), LDAP_OPT_X_TLS_CRL_NONE },
{ BER_BVC("peer"), LDAP_OPT_X_TLS_CRL_PEER },
{ BER_BVC("all"), LDAP_OPT_X_TLS_CRL_ALL },
{ BER_BVNULL, 0 }
};
static slap_verbmasks vfykeys[] = {
{ BER_BVC("never"), LDAP_OPT_X_TLS_NEVER },
{ BER_BVC("allow"), LDAP_OPT_X_TLS_ALLOW },
{ BER_BVC("try"), LDAP_OPT_X_TLS_TRY },
{ BER_BVC("demand"), LDAP_OPT_X_TLS_DEMAND },
{ BER_BVC("hard"), LDAP_OPT_X_TLS_HARD },
{ BER_BVC("true"), LDAP_OPT_X_TLS_HARD },
{ BER_BVNULL, 0 }
};
static slap_verbmasks methkey[] = {
{ BER_BVC("none"), LDAP_AUTH_NONE },
{ BER_BVC("simple"), LDAP_AUTH_SIMPLE },
#ifdef HAVE_CYRUS_SASL
{ BER_BVC("sasl"), LDAP_AUTH_SASL },
#endif
{ BER_BVNULL, 0 }
};
int
lload_keepalive_parse(
struct berval *val,
void *bc,
slap_cf_aux_table *tab0,
const char *tabmsg,
int unparse )
{
if ( unparse ) {
slap_keepalive *sk = (slap_keepalive *)bc;
int rc = snprintf( val->bv_val, val->bv_len, "%d:%d:%d",
sk->sk_idle, sk->sk_probes, sk->sk_interval );
if ( rc < 0 ) {
return -1;
}
if ( (unsigned)rc >= val->bv_len ) {
return -1;
}
val->bv_len = rc;
} else {
char *s = val->bv_val;
char *next;
slap_keepalive *sk = (slap_keepalive *)bc;
slap_keepalive sk2;
if ( s[0] == ':' ) {
sk2.sk_idle = 0;
s++;
} else {
sk2.sk_idle = strtol( s, &next, 10 );
if ( next == s || next[0] != ':' ) {
return -1;
}
if ( sk2.sk_idle < 0 ) {
return -1;
}
s = ++next;
}
if ( s[0] == ':' ) {
sk2.sk_probes = 0;
s++;
} else {
sk2.sk_probes = strtol( s, &next, 10 );
if ( next == s || next[0] != ':' ) {
return -1;
}
if ( sk2.sk_probes < 0 ) {
return -1;
}
s = ++next;
}
if ( *s == '\0' ) {
sk2.sk_interval = 0;
} else {
sk2.sk_interval = strtol( s, &next, 10 );
if ( next == s || next[0] != '\0' ) {
return -1;
}
if ( sk2.sk_interval < 0 ) {
return -1;
}
}
*sk = sk2;
ber_memfree( val->bv_val );
BER_BVZERO( val );
}
return 0;
}
static slap_cf_aux_table backendkey[] = {
{ BER_BVC("uri="), offsetof(LloadBackend, b_uri), 'b', 1, NULL },
{ BER_BVC("numconns="), offsetof(LloadBackend, b_numconns), 'i', 0, NULL },
{ BER_BVC("bindconns="), offsetof(LloadBackend, b_numbindconns), 'i', 0, NULL },
{ BER_BVC("retry="), offsetof(LloadBackend, b_retry_timeout), 'i', 0, NULL },
{ BER_BVC("max-pending-ops="), offsetof(LloadBackend, b_max_pending), 'i', 0, NULL },
{ BER_BVC("conn-max-pending="), offsetof(LloadBackend, b_max_conn_pending), 'i', 0, NULL },
{ BER_BVC("starttls="), offsetof(LloadBackend, b_tls_conf), 'i', 0, tlskey },
{ BER_BVNULL, 0, 0, 0, NULL }
};
static slap_cf_aux_table bindkey[] = {
{ BER_BVC("bindmethod="), offsetof(slap_bindconf, sb_method), 'i', 0, methkey },
{ BER_BVC("timeout="), offsetof(slap_bindconf, sb_timeout_api), 'i', 0, NULL },
{ BER_BVC("network-timeout="), offsetof(slap_bindconf, sb_timeout_net), 'i', 0, NULL },
{ BER_BVC("binddn="), offsetof(slap_bindconf, sb_binddn), 'b', 1, NULL },
{ BER_BVC("credentials="), offsetof(slap_bindconf, sb_cred), 'b', 1, NULL },
{ BER_BVC("saslmech="), offsetof(slap_bindconf, sb_saslmech), 'b', 0, NULL },
{ BER_BVC("secprops="), offsetof(slap_bindconf, sb_secprops), 's', 0, NULL },
{ BER_BVC("realm="), offsetof(slap_bindconf, sb_realm), 'b', 0, NULL },
{ BER_BVC("authcID="), offsetof(slap_bindconf, sb_authcId), 'b', 1, NULL },
{ BER_BVC("authzID="), offsetof(slap_bindconf, sb_authzId), 'b', 1, NULL },
{ BER_BVC("keepalive="), offsetof(slap_bindconf, sb_keepalive), 'x', 0, (slap_verbmasks *)lload_keepalive_parse },
#ifdef HAVE_TLS
/* NOTE: replace "11" with the actual index
* of the first TLS-related line */
#define aux_TLS (bindkey+11) /* beginning of TLS keywords */
{ BER_BVC("tls_cert="), offsetof(slap_bindconf, sb_tls_cert), 's', 1, NULL },
{ BER_BVC("tls_key="), offsetof(slap_bindconf, sb_tls_key), 's', 1, NULL },
{ BER_BVC("tls_cacert="), offsetof(slap_bindconf, sb_tls_cacert), 's', 1, NULL },
{ BER_BVC("tls_cacertdir="), offsetof(slap_bindconf, sb_tls_cacertdir), 's', 1, NULL },
{ BER_BVC("tls_reqcert="), offsetof(slap_bindconf, sb_tls_reqcert), 's', 0, NULL },
{ BER_BVC("tls_reqsan="), offsetof(slap_bindconf, sb_tls_reqsan), 's', 0, NULL },
{ BER_BVC("tls_cipher_suite="), offsetof(slap_bindconf, sb_tls_cipher_suite), 's', 0, NULL },
{ BER_BVC("tls_protocol_min="), offsetof(slap_bindconf, sb_tls_protocol_min), 's', 0, NULL },
{ BER_BVC("tls_ecname="), offsetof(slap_bindconf, sb_tls_ecname), 's', 0, NULL },
#ifdef HAVE_OPENSSL
{ BER_BVC("tls_crlcheck="), offsetof(slap_bindconf, sb_tls_crlcheck), 's', 0, NULL },
#endif
#endif
{ BER_BVNULL, 0, 0, 0, NULL }
};
/*
* 's': char *
* 'b': struct berval
* 'i': int; if !NULL, compute using ((slap_verbmasks *)aux)
* 'u': unsigned
* 'I': long
* 'U': unsigned long
*/
int
lload_cf_aux_table_parse(
const char *word,
void *dst,
slap_cf_aux_table *tab0,
LDAP_CONST char *tabmsg )
{
int rc = SLAP_CONF_UNKNOWN;
slap_cf_aux_table *tab;
for ( tab = tab0; !BER_BVISNULL( &tab->key ); tab++ ) {
if ( !strncasecmp( word, tab->key.bv_val, tab->key.bv_len ) ) {
char **cptr;
int *iptr, j;
unsigned *uptr;
long *lptr;
unsigned long *ulptr;
struct berval *bptr;
const char *val = word + tab->key.bv_len;
switch ( tab->type ) {
case 's':
cptr = (char **)( (char *)dst + tab->off );
*cptr = ch_strdup( val );
rc = 0;
break;
case 'b':
bptr = (struct berval *)( (char *)dst + tab->off );
assert( tab->aux == NULL );
ber_str2bv( val, 0, 1, bptr );
rc = 0;
break;
case 'i':
iptr = (int *)( (char *)dst + tab->off );
if ( tab->aux != NULL ) {
slap_verbmasks *aux = (slap_verbmasks *)tab->aux;
assert( aux != NULL );
rc = 1;
for ( j = 0; !BER_BVISNULL( &aux[j].word ); j++ ) {
if ( !strcasecmp( val, aux[j].word.bv_val ) ) {
*iptr = aux[j].mask;
rc = 0;
break;
}
}
} else {
rc = lutil_atoix( iptr, val, 0 );
}
break;
case 'u':
uptr = (unsigned *)( (char *)dst + tab->off );
rc = lutil_atoux( uptr, val, 0 );
break;
case 'I':
lptr = (long *)( (char *)dst + tab->off );
rc = lutil_atolx( lptr, val, 0 );
break;
case 'U':
ulptr = (unsigned long *)( (char *)dst + tab->off );
rc = lutil_atoulx( ulptr, val, 0 );
break;
case 'x':
if ( tab->aux != NULL ) {
struct berval value;
lload_cf_aux_table_parse_x *func =
(lload_cf_aux_table_parse_x *)tab->aux;
ber_str2bv( val, 0, 1, &value );
rc = func( &value, (void *)( (char *)dst + tab->off ),
tab, tabmsg, 0 );
} else {
rc = 1;
}
break;
}
if ( rc ) {
Debug( LDAP_DEBUG_ANY, "invalid %s value %s\n", tabmsg, word );
}
return rc;
}
}
return rc;
}
int
lload_cf_aux_table_unparse(
void *src,
struct berval *bv,
slap_cf_aux_table *tab0 )
{
char buf[AC_LINE_MAX], *ptr;
slap_cf_aux_table *tab;
struct berval tmp;
ptr = buf;
for ( tab = tab0; !BER_BVISNULL( &tab->key ); tab++ ) {
char **cptr;
int *iptr, i;
unsigned *uptr;
long *lptr;
unsigned long *ulptr;
struct berval *bptr;
cptr = (char **)( (char *)src + tab->off );
switch ( tab->type ) {
case 'b':
bptr = (struct berval *)( (char *)src + tab->off );
cptr = &bptr->bv_val;
case 's':
if ( *cptr ) {
*ptr++ = ' ';
ptr = lutil_strcopy( ptr, tab->key.bv_val );
if ( tab->quote ) *ptr++ = '"';
ptr = lutil_strcopy( ptr, *cptr );
if ( tab->quote ) *ptr++ = '"';
}
break;
case 'i':
iptr = (int *)( (char *)src + tab->off );
if ( tab->aux != NULL ) {
slap_verbmasks *aux = (slap_verbmasks *)tab->aux;
for ( i = 0; !BER_BVISNULL( &aux[i].word ); i++ ) {
if ( *iptr == aux[i].mask ) {
*ptr++ = ' ';
ptr = lutil_strcopy( ptr, tab->key.bv_val );
ptr = lutil_strcopy( ptr, aux[i].word.bv_val );
break;
}
}
} else {
*ptr++ = ' ';
ptr = lutil_strcopy( ptr, tab->key.bv_val );
ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%d",
*iptr );
}
break;
case 'u':
uptr = (unsigned *)( (char *)src + tab->off );
*ptr++ = ' ';
ptr = lutil_strcopy( ptr, tab->key.bv_val );
ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%u",
*uptr );
break;
case 'I':
lptr = (long *)( (char *)src + tab->off );
*ptr++ = ' ';
ptr = lutil_strcopy( ptr, tab->key.bv_val );
ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%ld",
*lptr );
break;
case 'U':
ulptr = (unsigned long *)( (char *)src + tab->off );
*ptr++ = ' ';
ptr = lutil_strcopy( ptr, tab->key.bv_val );
ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%lu",
*ulptr );
break;
case 'x': {
char *saveptr = ptr;
*ptr++ = ' ';
ptr = lutil_strcopy( ptr, tab->key.bv_val );
if ( tab->quote ) *ptr++ = '"';
if ( tab->aux != NULL ) {
struct berval value;
lload_cf_aux_table_parse_x *func =
(lload_cf_aux_table_parse_x *)tab->aux;
int rc;
value.bv_val = ptr;
value.bv_len = buf + sizeof(buf) - ptr;
rc = func( &value, (void *)( (char *)src + tab->off ), tab,
"(unparse)", 1 );
if ( rc == 0 ) {
if ( value.bv_len ) {
ptr += value.bv_len;
} else {
ptr = saveptr;
break;
}
}
}
if ( tab->quote ) *ptr++ = '"';
} break;
default:
assert(0);
}
}
tmp.bv_val = buf;
tmp.bv_len = ptr - buf;
ber_dupbv( bv, &tmp );
return 0;
}
int
lload_tls_get_config( LDAP *ld, int opt, char **val )
{
#ifdef HAVE_TLS
slap_verbmasks *keys;
int i, ival;
*val = NULL;
switch ( opt ) {
case LDAP_OPT_X_TLS_CRLCHECK:
keys = crlkeys;
break;
case LDAP_OPT_X_TLS_REQUIRE_CERT:
keys = vfykeys;
break;
case LDAP_OPT_X_TLS_PROTOCOL_MIN: {
char buf[8];
ldap_pvt_tls_get_option( ld, opt, &ival );
snprintf( buf, sizeof(buf), "%d.%d",
( ival >> 8 ) & 0xff, ival & 0xff );
*val = ch_strdup( buf );
return 0;
}
default:
return -1;
}
ldap_pvt_tls_get_option( ld, opt, &ival );
for ( i = 0; !BER_BVISNULL( &keys[i].word ); i++ ) {
if ( keys[i].mask == ival ) {
*val = ch_strdup( keys[i].word.bv_val );
return 0;
}
}
#endif
return -1;
}
#ifdef HAVE_TLS
static struct {
const char *key;
size_t offset;
int opt;
} bindtlsopts[] = {
{ "tls_cert", offsetof(slap_bindconf, sb_tls_cert), LDAP_OPT_X_TLS_CERTFILE },
{ "tls_key", offsetof(slap_bindconf, sb_tls_key), LDAP_OPT_X_TLS_KEYFILE },
{ "tls_cacert", offsetof(slap_bindconf, sb_tls_cacert), LDAP_OPT_X_TLS_CACERTFILE },
{ "tls_cacertdir", offsetof(slap_bindconf, sb_tls_cacertdir), LDAP_OPT_X_TLS_CACERTDIR },
{ "tls_cipher_suite", offsetof(slap_bindconf, sb_tls_cipher_suite), LDAP_OPT_X_TLS_CIPHER_SUITE },
{ "tls_ecname", offsetof(slap_bindconf, sb_tls_ecname), LDAP_OPT_X_TLS_ECNAME },
{ NULL, 0 }
};
int
lload_bindconf_tls_set( slap_bindconf *bc, LDAP *ld )
{
int i, rc, newctx = 0, res = 0;
char *ptr = (char *)bc, **word;
if ( bc->sb_tls_do_init ) {
for ( i = 0; bindtlsopts[i].opt; i++ ) {
word = (char **)( ptr + bindtlsopts[i].offset );
if ( *word ) {
rc = ldap_set_option( ld, bindtlsopts[i].opt, *word );
if ( rc ) {
Debug( LDAP_DEBUG_ANY, "lload_bindconf_tls_set: "
"failed to set %s to %s\n",
bindtlsopts[i].key, *word );
res = -1;
} else
newctx = 1;
}
}
if ( bc->sb_tls_reqcert ) {
rc = ldap_pvt_tls_config(
ld, LDAP_OPT_X_TLS_REQUIRE_CERT, bc->sb_tls_reqcert );
if ( rc ) {
Debug( LDAP_DEBUG_ANY, "lload_bindconf_tls_set: "
"failed to set tls_reqcert to %s\n",
bc->sb_tls_reqcert );
res = -1;
} else {
newctx = 1;
/* retrieve the parsed setting for later use */
ldap_get_option( ld, LDAP_OPT_X_TLS_REQUIRE_CERT,
&bc->sb_tls_int_reqcert );
}
}
if ( bc->sb_tls_reqsan ) {
rc = ldap_pvt_tls_config(
ld, LDAP_OPT_X_TLS_REQUIRE_SAN, bc->sb_tls_reqsan );
if ( rc ) {
Debug( LDAP_DEBUG_ANY, "lload_bindconf_tls_set: "
"failed to set tls_reqsan to %s\n",
bc->sb_tls_reqsan );
res = -1;
} else {
newctx = 1;
/* retrieve the parsed setting for later use */
ldap_get_option( ld, LDAP_OPT_X_TLS_REQUIRE_SAN,
&bc->sb_tls_int_reqsan );
}
}
if ( bc->sb_tls_protocol_min ) {
rc = ldap_pvt_tls_config(
ld, LDAP_OPT_X_TLS_PROTOCOL_MIN, bc->sb_tls_protocol_min );
if ( rc ) {
Debug( LDAP_DEBUG_ANY, "lload_bindconf_tls_set: "
"failed to set tls_protocol_min to %s\n",
bc->sb_tls_protocol_min );
res = -1;
} else
newctx = 1;
}
#ifdef HAVE_OPENSSL
if ( bc->sb_tls_crlcheck ) {
rc = ldap_pvt_tls_config(
ld, LDAP_OPT_X_TLS_CRLCHECK, bc->sb_tls_crlcheck );
if ( rc ) {
Debug( LDAP_DEBUG_ANY, "lload_bindconf_tls_set: "
"failed to set tls_crlcheck to %s\n",
bc->sb_tls_crlcheck );
res = -1;
} else
newctx = 1;
}
#endif
if ( !res ) bc->sb_tls_do_init = 0;
}
if ( newctx ) {
int opt = 0;
if ( bc->sb_tls_ctx ) {
ldap_pvt_tls_ctx_free( bc->sb_tls_ctx );
bc->sb_tls_ctx = NULL;
}
rc = ldap_set_option( ld, LDAP_OPT_X_TLS_NEWCTX, &opt );
if ( rc )
res = rc;
else
ldap_get_option( ld, LDAP_OPT_X_TLS_CTX, &bc->sb_tls_ctx );
} else if ( bc->sb_tls_ctx ) {
rc = ldap_set_option( ld, LDAP_OPT_X_TLS_CTX, bc->sb_tls_ctx );
if ( rc == LDAP_SUCCESS ) {
/* these options aren't actually inside the ctx, so have to be set again */
ldap_set_option(
ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &bc->sb_tls_int_reqcert );
ldap_set_option(
ld, LDAP_OPT_X_TLS_REQUIRE_SAN, &bc->sb_tls_int_reqsan );
} else
res = rc;
}
return res;
}
#endif
int
lload_bindconf_tls_parse( const char *word, slap_bindconf *bc )
{
#ifdef HAVE_TLS
if ( lload_cf_aux_table_parse( word, bc, aux_TLS, "tls config" ) == 0 ) {
bc->sb_tls_do_init = 1;
return 0;
}
#endif
return -1;
}
int
lload_backend_parse( const char *word, LloadBackend *b )
{
return lload_cf_aux_table_parse( word, b, backendkey, "backend config" );
}
int
lload_bindconf_parse( const char *word, slap_bindconf *bc )
{
#ifdef HAVE_TLS
/* Detect TLS config changes explicitly */
if ( lload_bindconf_tls_parse( word, bc ) == 0 ) {
return 0;
}
#endif
return lload_cf_aux_table_parse( word, bc, bindkey, "bind config" );
}
int
lload_bindconf_unparse( slap_bindconf *bc, struct berval *bv )
{
return lload_cf_aux_table_unparse( bc, bv, bindkey );
}
void
lload_bindconf_free( slap_bindconf *bc )
{
if ( !BER_BVISNULL( &bc->sb_uri ) ) {
ch_free( bc->sb_uri.bv_val );
BER_BVZERO( &bc->sb_uri );
}
if ( !BER_BVISNULL( &bc->sb_binddn ) ) {
ch_free( bc->sb_binddn.bv_val );
BER_BVZERO( &bc->sb_binddn );
}
if ( !BER_BVISNULL( &bc->sb_cred ) ) {
ch_free( bc->sb_cred.bv_val );
BER_BVZERO( &bc->sb_cred );
}
if ( !BER_BVISNULL( &bc->sb_saslmech ) ) {
ch_free( bc->sb_saslmech.bv_val );
BER_BVZERO( &bc->sb_saslmech );
}
if ( bc->sb_secprops ) {
ch_free( bc->sb_secprops );
bc->sb_secprops = NULL;
}
if ( !BER_BVISNULL( &bc->sb_realm ) ) {
ch_free( bc->sb_realm.bv_val );
BER_BVZERO( &bc->sb_realm );
}
if ( !BER_BVISNULL( &bc->sb_authcId ) ) {
ch_free( bc->sb_authcId.bv_val );
BER_BVZERO( &bc->sb_authcId );
}
if ( !BER_BVISNULL( &bc->sb_authzId ) ) {
ch_free( bc->sb_authzId.bv_val );
BER_BVZERO( &bc->sb_authzId );
}
#ifdef HAVE_TLS
if ( bc->sb_tls_cert ) {
ch_free( bc->sb_tls_cert );
bc->sb_tls_cert = NULL;
}
if ( bc->sb_tls_key ) {
ch_free( bc->sb_tls_key );
bc->sb_tls_key = NULL;
}
if ( bc->sb_tls_cacert ) {
ch_free( bc->sb_tls_cacert );
bc->sb_tls_cacert = NULL;
}
if ( bc->sb_tls_cacertdir ) {
ch_free( bc->sb_tls_cacertdir );
bc->sb_tls_cacertdir = NULL;
}
if ( bc->sb_tls_reqcert ) {
ch_free( bc->sb_tls_reqcert );
bc->sb_tls_reqcert = NULL;
}
if ( bc->sb_tls_cipher_suite ) {
ch_free( bc->sb_tls_cipher_suite );
bc->sb_tls_cipher_suite = NULL;
}
if ( bc->sb_tls_protocol_min ) {
ch_free( bc->sb_tls_protocol_min );
bc->sb_tls_protocol_min = NULL;
}
#ifdef HAVE_OPENSSL_CRL
if ( bc->sb_tls_crlcheck ) {
ch_free( bc->sb_tls_crlcheck );
bc->sb_tls_crlcheck = NULL;
}
#endif
if ( bc->sb_tls_ctx ) {
ldap_pvt_tls_ctx_free( bc->sb_tls_ctx );
bc->sb_tls_ctx = NULL;
}
#endif
}
void
lload_bindconf_tls_defaults( slap_bindconf *bc )
{
#ifdef HAVE_TLS
if ( bc->sb_tls_do_init ) {
if ( !bc->sb_tls_cacert )
ldap_pvt_tls_get_option( lload_tls_ld, LDAP_OPT_X_TLS_CACERTFILE,
&bc->sb_tls_cacert );
if ( !bc->sb_tls_cacertdir )
ldap_pvt_tls_get_option( lload_tls_ld, LDAP_OPT_X_TLS_CACERTDIR,
&bc->sb_tls_cacertdir );
if ( !bc->sb_tls_cert )
ldap_pvt_tls_get_option(
lload_tls_ld, LDAP_OPT_X_TLS_CERTFILE, &bc->sb_tls_cert );
if ( !bc->sb_tls_key )
ldap_pvt_tls_get_option(
lload_tls_ld, LDAP_OPT_X_TLS_KEYFILE, &bc->sb_tls_key );
if ( !bc->sb_tls_cipher_suite )
ldap_pvt_tls_get_option( lload_tls_ld, LDAP_OPT_X_TLS_CIPHER_SUITE,
&bc->sb_tls_cipher_suite );
if ( !bc->sb_tls_reqcert ) bc->sb_tls_reqcert = ch_strdup( "demand" );
#ifdef HAVE_OPENSSL_CRL
if ( !bc->sb_tls_crlcheck )
lload_tls_get_config( lload_tls_ld, LDAP_OPT_X_TLS_CRLCHECK,
&bc->sb_tls_crlcheck );
#endif
}
#endif
}
/* -------------------------------------- */
static char *
strtok_quote( char *line, char *sep, char **quote_ptr, int *iqp )
{
int inquote;
char *tmp;
static char *next;
*quote_ptr = NULL;
if ( line != NULL ) {
next = line;
}
while ( *next && strchr( sep, *next ) ) {
next++;
}
if ( *next == '\0' ) {
next = NULL;
return NULL;
}
tmp = next;
for ( inquote = 0; *next; ) {
switch ( *next ) {
case '"':
if ( inquote ) {
inquote = 0;
} else {
inquote = 1;
}
AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
break;
case '\\':
if ( next[1] )
AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
next++; /* dont parse the escaped character */
break;
default:
if ( !inquote ) {
if ( strchr( sep, *next ) != NULL ) {
*quote_ptr = next;
*next++ = '\0';
return tmp;
}
}
next++;
break;
}
}
*iqp = inquote;
return tmp;
}
static char buf[AC_LINE_MAX];
static char *line;
static size_t lmax, lcur;
#define CATLINE( buf ) \
do { \
size_t len = strlen( buf ); \
while ( lcur + len + 1 > lmax ) { \
lmax += AC_LINE_MAX; \
line = (char *)ch_realloc( line, lmax ); \
} \
strcpy( line + lcur, buf ); \
lcur += len; \
} while (0)
static void
fp_getline_init( ConfigArgs *c )
{
c->lineno = -1;
buf[0] = '\0';
}
static int
fp_getline( FILE *fp, ConfigArgs *c )
{
char *p;
lcur = 0;
CATLINE( buf );
c->lineno++;
/* avoid stack of bufs */
if ( strncasecmp( line, "include", STRLENOF("include") ) == 0 ) {
buf[0] = '\0';
c->line = line;
return 1;
}
while ( fgets( buf, sizeof(buf), fp ) ) {
p = strchr( buf, '\n' );
if ( p ) {
if ( p > buf && p[-1] == '\r' ) {
--p;
}
*p = '\0';
}
/* XXX ugly */
c->line = line;
if ( line[0] && ( p = line + strlen( line ) - 1 )[0] == '\\' &&
p[-1] != '\\' ) {
p[0] = '\0';
lcur--;
} else {
if ( !isspace( (unsigned char)buf[0] ) ) {
return 1;
}
buf[0] = ' ';
}
CATLINE( buf );
c->lineno++;
}
buf[0] = '\0';
c->line = line;
return ( line[0] ? 1 : 0 );
}
int
lload_config_fp_parse_line( ConfigArgs *c )
{
char *token;
static char *const hide[] = { "bindconf", NULL };
static char *const raw[] = { NULL };
char *quote_ptr;
int i = (int)( sizeof(hide) / sizeof(hide[0]) ) - 1;
int inquote = 0;
c->tline = ch_strdup( c->line );
c->linelen = strlen( c->line );
token = strtok_quote( c->tline, " \t", &quote_ptr, &inquote );
if ( token )
for ( i = 0; hide[i]; i++ )
if ( !strcasecmp( token, hide[i] ) ) break;
if ( quote_ptr ) *quote_ptr = ' ';
Debug( LDAP_DEBUG_CONFIG, "%s (%s%s)\n",
c->log, hide[i] ? hide[i] : c->line, hide[i] ? " ***" : "" );
if ( quote_ptr ) *quote_ptr = '\0';
for ( ;; token = strtok_quote( NULL, " \t", &quote_ptr, &inquote ) ) {
if ( c->argc >= c->argv_size ) {
char **tmp;
tmp = ch_realloc( c->argv,
( c->argv_size + ARGS_STEP ) * sizeof(*c->argv) );
if ( !tmp ) {
Debug( LDAP_DEBUG_ANY, "%s: out of memory\n", c->log );
return -1;
}
c->argv = tmp;
c->argv_size += ARGS_STEP;
}
if ( token == NULL ) break;
c->argv[c->argc++] = token;
}
c->argv[c->argc] = NULL;
if ( inquote ) {
/* these directives parse c->line independently of argv tokenizing */
for ( i = 0; raw[i]; i++ )
if ( !strcasecmp( c->argv[0], raw[i] ) ) return 0;
Debug( LDAP_DEBUG_ANY, "%s: unterminated quoted string \"%s\"\n",
c->log, c->argv[c->argc - 1] );
return -1;
}
return 0;
}
void
lload_config_destroy( void )
{
free( line );
if ( slapd_args_file ) free( slapd_args_file );
if ( slapd_pid_file ) free( slapd_pid_file );
loglevel_destroy();
}
/* See if the given URL (in plain and parsed form) matches
* any of the server's listener addresses. Return matching
* LloadListener or NULL for no match.
*/
LloadListener *
lload_config_check_my_url( const char *url, LDAPURLDesc *lud )
{
LloadListener **l = lloadd_get_listeners();
int i, isMe;
/* Try a straight compare with LloadListener strings */
for ( i = 0; l && l[i]; i++ ) {
if ( !strcasecmp( url, l[i]->sl_url.bv_val ) ) {
return l[i];
}
}
isMe = 0;
/* If hostname is empty, or is localhost, or matches
* our hostname, this url refers to this host.
* Compare it against listeners and ports.
*/
if ( !lud->lud_host || !lud->lud_host[0] ||
!strncasecmp(
"localhost", lud->lud_host, STRLENOF("localhost") ) ||
!strcasecmp( global_host, lud->lud_host ) ) {
for ( i = 0; l && l[i]; i++ ) {
LDAPURLDesc *lu2;
ldap_url_parse_ext(
l[i]->sl_url.bv_val, &lu2, LDAP_PVT_URL_PARSE_DEF_PORT );
do {
if ( strcasecmp( lud->lud_scheme, lu2->lud_scheme ) ) break;
if ( lud->lud_port != lu2->lud_port ) break;
/* Listener on ANY address */
if ( !lu2->lud_host || !lu2->lud_host[0] ) {
isMe = 1;
break;
}
/* URL on ANY address */
if ( !lud->lud_host || !lud->lud_host[0] ) {
isMe = 1;
break;
}
/* Listener has specific host, must
* match it
*/
if ( !strcasecmp( lud->lud_host, lu2->lud_host ) ) {
isMe = 1;
break;
}
} while (0);
ldap_free_urldesc( lu2 );
if ( isMe ) {
return l[i];
}
}
}
return NULL;
}
#ifdef BALANCER_MODULE
static int
backend_cf_gen( ConfigArgs *c )
{
LloadBackend *b = c->ca_private;
enum lcf_backend flag = 0;
int rc = LDAP_SUCCESS;
assert( b != NULL );
if ( c->op == SLAP_CONFIG_EMIT ) {
switch ( c->type ) {
case CFG_URI:
c->value_bv = b->b_uri;
break;
case CFG_NUMCONNS:
c->value_uint = b->b_numconns;
break;
case CFG_BINDCONNS:
c->value_uint = b->b_numbindconns;
break;
case CFG_RETRY:
c->value_uint = b->b_retry_timeout;
break;
case CFG_MAX_PENDING_CONNS:
c->value_uint = b->b_max_conn_pending;
break;
case CFG_MAX_PENDING_OPS:
c->value_uint = b->b_max_pending;
break;
case CFG_STARTTLS:
enum_to_verb( tlskey, b->b_tls_conf, &c->value_bv );
break;
default:
rc = 1;
break;
}
return rc;
} else if ( c->op == LDAP_MOD_DELETE ) {
/* We only need to worry about deletions to multi-value or MAY
* attributes */
switch ( c->type ) {
case CFG_STARTTLS:
b->b_tls_conf = LLOAD_CLEARTEXT;
break;
default:
break;
}
return rc;
}
switch ( c->type ) {
case CFG_URI:
rc = backend_config_url( b, &c->value_bv );
if ( rc ) {
backend_config_url( b, &b->b_uri );
goto fail;
}
if ( !BER_BVISNULL( &b->b_uri ) ) {
ch_free( b->b_uri.bv_val );
}
b->b_uri = c->value_bv;
flag = LLOAD_BACKEND_MOD_OTHER;
break;
case CFG_NUMCONNS:
if ( !c->value_uint ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"invalid connection pool configuration" );
goto fail;
}
b->b_numconns = c->value_uint;
flag = LLOAD_BACKEND_MOD_CONNS;
break;
case CFG_BINDCONNS:
if ( !c->value_uint ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"invalid connection pool configuration" );
goto fail;
}
b->b_numbindconns = c->value_uint;
flag = LLOAD_BACKEND_MOD_CONNS;
break;
case CFG_RETRY:
b->b_retry_timeout = c->value_uint;
break;
case CFG_MAX_PENDING_CONNS:
b->b_max_conn_pending = c->value_uint;
break;
case CFG_MAX_PENDING_OPS:
b->b_max_pending = c->value_uint;
break;
case CFG_STARTTLS: {
int i = bverb_to_mask( &c->value_bv, tlskey );
if ( BER_BVISNULL( &tlskey[i].word ) ) {
snprintf( c->cr_msg, sizeof(c->cr_msg),
"invalid starttls configuration" );
goto fail;
}
b->b_tls_conf = tlskey[i].mask;
} break;
default:
rc = 1;
break;
}
/* do not set this if it has already been set by another callback, e.g.
* lload_backend_ldadd */
if ( lload_change.type == LLOAD_CHANGE_UNDEFINED ) {
lload_change.type = LLOAD_CHANGE_MODIFY;
}
lload_change.object = LLOAD_BACKEND;
lload_change.target = b;
lload_change.flags.backend |= flag;
config_push_cleanup( c, lload_backend_finish );
return rc;
fail:
if ( lload_change.type == LLOAD_CHANGE_ADD ) {
/* Abort the ADD */
lload_change.type = LLOAD_CHANGE_DEL;
}
Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
return 1;
}
int
lload_back_init_cf( BackendInfo *bi )
{
/* Make sure we don't exceed the bits reserved for userland */
config_check_userland( CFG_LAST );
bi->bi_cf_ocs = lloadocs;
return config_register_schema( config_back_cf_table, lloadocs );
}
static int
lload_backend_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
{
LloadBackend *b;
Attribute *a;
AttributeDescription *ad = NULL;
struct berval bv, type, rdn;
const char *text;
char *name;
Debug( LDAP_DEBUG_TRACE, "lload_backend_ldadd: "
"a new backend-server is being added\n" );
if ( p->ce_type != Cft_Backend || !p->ce_bi ||
p->ce_bi->bi_cf_ocs != lloadocs )
return LDAP_CONSTRAINT_VIOLATION;
dnRdn( &e->e_name, &rdn );
type.bv_len = strchr( rdn.bv_val, '=' ) - rdn.bv_val;
type.bv_val = rdn.bv_val;
/* Find attr */
slap_bv2ad( &type, &ad, &text );
if ( ad != slap_schema.si_ad_cn ) return LDAP_NAMING_VIOLATION;
a = attr_find( e->e_attrs, ad );
if ( !a || a->a_numvals != 1 ) return LDAP_NAMING_VIOLATION;
bv = a->a_vals[0];
if ( bv.bv_val[0] == '{' && ( name = strchr( bv.bv_val, '}' ) ) ) {
name++;
bv.bv_len -= name - bv.bv_val;
bv.bv_val = name;
}
b = backend_alloc();
ber_dupbv( &b->b_name, &bv );
ca->bi = p->ce_bi;
ca->ca_private = b;
config_push_cleanup( ca, lload_backend_finish );
/* ca cleanups are only run in the case of online config but we use it to
* save the new config when done with the entry */
ca->lineno = 0;
lload_change.type = LLOAD_CHANGE_ADD;
lload_change.object = LLOAD_BACKEND;
lload_change.target = b;
return LDAP_SUCCESS;
}
#ifdef SLAP_CONFIG_DELETE
static int
lload_backend_lddel( CfEntryInfo *ce, Operation *op )
{
LloadBackend *b = ce->ce_private;
lload_change.type = LLOAD_CHANGE_DEL;
lload_change.object = LLOAD_BACKEND;
lload_change.target = b;
return LDAP_SUCCESS;
}
#endif /* SLAP_CONFIG_DELETE */
static int
lload_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *c )
{
struct berval bv;
LloadBackend *b;
int i = 0;
bv.bv_val = c->cr_msg;
LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
char buf[STRLENOF( "server 4294967295" ) + 1] = { 0 };
bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg),
"cn=" SLAP_X_ORDERED_FMT "server %d", i, i + 1 );
snprintf( buf, sizeof(buf), "server %d", i + 1 );
ber_str2bv( buf, 0, 1, &b->b_name );
c->ca_private = b;
c->valx = i;
config_build_entry( op, rs, p->e_private, c, &bv, &lloadocs[1], NULL );
i++;
}
return LDAP_SUCCESS;
}
#endif /* BALANCER_MODULE */