#include <config.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <unistd.h>
#include <time.h>
#include <ctype.h>
#include <fcntl.h>
#ifdef HAVE_LBER_H
# include <lber.h>
#endif
#include <ldap.h>
#if defined(HAVE_LDAP_SSL_H)
# include <ldap_ssl.h>
#elif defined(HAVE_MPS_LDAP_SSL_H)
# include <mps/ldap_ssl.h>
#endif
#include "sudoers.h"
#include "sudo_lbuf.h"
#include "sudo_ldap.h"
#include "sudo_ldap_conf.h"
#if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(HAVE_LDAP_SSL_H) && !defined(HAVE_MPS_LDAP_SSL_H)
extern int ldapssl_set_strength(LDAP *ldap, int strength);
#endif
#if !defined(LDAP_OPT_NETWORK_TIMEOUT) && defined(LDAP_OPT_CONNECT_TIMEOUT)
# define LDAP_OPT_NETWORK_TIMEOUT LDAP_OPT_CONNECT_TIMEOUT
#endif
#ifndef LDAP_OPT_SUCCESS
# define LDAP_OPT_SUCCESS LDAP_SUCCESS
#endif
#ifndef LDAPS_PORT
# define LDAPS_PORT 636
#endif
#define DEFAULT_SEARCH_FILTER "(objectClass=sudoRole)"
#define DEFAULT_NETGROUP_SEARCH_FILTER "(objectClass=nisNetgroup)"
struct ldap_config ldap_conf;
static struct ldap_config_table ldap_conf_global[] = {
{ "sudoers_debug", CONF_INT, -1, &ldap_conf.debug },
{ "host", CONF_STR, -1, &ldap_conf.host },
{ "port", CONF_INT, -1, &ldap_conf.port },
{ "ssl", CONF_STR, -1, &ldap_conf.ssl },
{ "sslpath", CONF_STR, -1, &ldap_conf.tls_certfile },
{ "uri", CONF_LIST_STR, -1, &ldap_conf.uri },
#ifdef LDAP_OPT_DEBUG_LEVEL
{ "debug", CONF_INT, LDAP_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug },
#endif
#ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
{ "tls_checkpeer", CONF_BOOL, LDAP_OPT_X_TLS_REQUIRE_CERT,
&ldap_conf.tls_checkpeer },
{ "tls_reqcert", CONF_REQCERT_VAL, LDAP_OPT_X_TLS_REQUIRE_CERT,
&ldap_conf.tls_reqcert },
#else
{ "tls_checkpeer", CONF_BOOL, -1, &ldap_conf.tls_checkpeer },
#endif
#ifdef LDAP_OPT_X_TLS_CACERTFILE
{ "tls_cacertfile", CONF_STR, LDAP_OPT_X_TLS_CACERTFILE,
&ldap_conf.tls_cacertfile },
{ "tls_cacert", CONF_STR, LDAP_OPT_X_TLS_CACERTFILE,
&ldap_conf.tls_cacertfile },
#endif
#ifdef LDAP_OPT_X_TLS_CACERTDIR
{ "tls_cacertdir", CONF_STR, LDAP_OPT_X_TLS_CACERTDIR,
&ldap_conf.tls_cacertdir },
#endif
#ifdef LDAP_OPT_X_TLS_RANDOM_FILE
{ "tls_randfile", CONF_STR, LDAP_OPT_X_TLS_RANDOM_FILE,
&ldap_conf.tls_random_file },
#endif
#ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
{ "tls_ciphers", CONF_STR, LDAP_OPT_X_TLS_CIPHER_SUITE,
&ldap_conf.tls_cipher_suite },
#elif defined(LDAP_OPT_SSL_CIPHER)
{ "tls_ciphers", CONF_STR, LDAP_OPT_SSL_CIPHER,
&ldap_conf.tls_cipher_suite },
#endif
#ifdef LDAP_OPT_X_TLS_CERTFILE
{ "tls_cert", CONF_STR, LDAP_OPT_X_TLS_CERTFILE,
&ldap_conf.tls_certfile },
#else
{ "tls_cert", CONF_STR, -1, &ldap_conf.tls_certfile },
#endif
#ifdef LDAP_OPT_X_TLS_KEYFILE
{ "tls_key", CONF_STR, LDAP_OPT_X_TLS_KEYFILE,
&ldap_conf.tls_keyfile },
#else
{ "tls_key", CONF_STR, -1, &ldap_conf.tls_keyfile },
#endif
#ifdef HAVE_LDAP_SSL_CLIENT_INIT
{ "tls_keypw", CONF_STR, -1, &ldap_conf.tls_keypw },
#endif
{ "binddn", CONF_STR, -1, &ldap_conf.binddn },
{ "bindpw", CONF_STR, -1, &ldap_conf.bindpw },
{ "rootbinddn", CONF_STR, -1, &ldap_conf.rootbinddn },
{ "sudoers_base", CONF_LIST_STR, -1, &ldap_conf.base },
{ "sudoers_timed", CONF_BOOL, -1, &ldap_conf.timed },
{ "sudoers_search_filter", CONF_STR, -1, &ldap_conf.search_filter },
{ "netgroup_base", CONF_LIST_STR, -1, &ldap_conf.netgroup_base },
{ "netgroup_search_filter", CONF_STR, -1, &ldap_conf.netgroup_search_filter },
#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
{ "use_sasl", CONF_BOOL, -1, &ldap_conf.use_sasl },
{ "sasl_mech", CONF_STR, -1, &ldap_conf.sasl_mech },
{ "sasl_auth_id", CONF_STR, -1, &ldap_conf.sasl_auth_id },
{ "rootuse_sasl", CONF_BOOL, -1, &ldap_conf.rootuse_sasl },
{ "rootsasl_auth_id", CONF_STR, -1, &ldap_conf.rootsasl_auth_id },
{ "krb5_ccname", CONF_STR, -1, &ldap_conf.krb5_ccname },
#endif
{ NULL }
};
static struct ldap_config_table ldap_conf_conn[] = {
#ifdef LDAP_OPT_PROTOCOL_VERSION
{ "ldap_version", CONF_INT, LDAP_OPT_PROTOCOL_VERSION,
&ldap_conf.version },
#endif
#ifdef LDAP_OPT_NETWORK_TIMEOUT
{ "bind_timelimit", CONF_INT, -1 ,
&ldap_conf.bind_timelimit },
{ "network_timeout", CONF_INT, -1 ,
&ldap_conf.bind_timelimit },
#elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
{ "bind_timelimit", CONF_INT, LDAP_X_OPT_CONNECT_TIMEOUT,
&ldap_conf.bind_timelimit },
{ "network_timeout", CONF_INT, LDAP_X_OPT_CONNECT_TIMEOUT,
&ldap_conf.bind_timelimit },
#endif
{ "timelimit", CONF_INT, LDAP_OPT_TIMELIMIT, &ldap_conf.timelimit },
#ifdef LDAP_OPT_TIMEOUT
{ "timeout", CONF_INT, -1 ,
&ldap_conf.timeout },
#endif
#ifdef LDAP_OPT_DEREF
{ "deref", CONF_DEREF_VAL, LDAP_OPT_DEREF, &ldap_conf.deref },
#endif
#ifdef LDAP_OPT_X_SASL_SECPROPS
{ "sasl_secprops", CONF_STR, LDAP_OPT_X_SASL_SECPROPS,
&ldap_conf.sasl_secprops },
#endif
{ NULL }
};
#ifdef HAVE_LDAP_CREATE
static bool
sudo_ldap_conf_add_ports(void)
{
char *host, *last, *port, defport[13];
char hostbuf[LINE_MAX * 2];
int len;
debug_decl(sudo_ldap_conf_add_ports, SUDOERS_DEBUG_LDAP)
hostbuf[0] = '\0';
len = snprintf(defport, sizeof(defport), ":%d", ldap_conf.port);
if (len < 0 || len >= ssizeof(defport)) {
sudo_warnx(U_("sudo_ldap_conf_add_ports: port too large"));
debug_return_bool(false);
}
for ((host = strtok_r(ldap_conf.host, " \t", &last)); host; (host = strtok_r(NULL, " \t", &last))) {
if (hostbuf[0] != '\0')
CHECK_STRLCAT(hostbuf, " ", sizeof(hostbuf));
CHECK_STRLCAT(hostbuf, host, sizeof(hostbuf));
if ((port = strrchr(host, ':')) == NULL ||
!isdigit((unsigned char)port[1])) {
CHECK_STRLCAT(hostbuf, defport, sizeof(hostbuf));
}
}
free(ldap_conf.host);
if ((ldap_conf.host = strdup(hostbuf)) == NULL)
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return_bool(ldap_conf.host != NULL);
overflow:
sudo_warnx(U_("internal error, %s overflow"), __func__);
debug_return_bool(false);
}
#endif
#ifndef HAVE_LDAP_INITIALIZE
static int
sudo_ldap_parse_uri(const struct ldap_config_str_list *uri_list)
{
const struct ldap_config_str *entry;
char *buf, hostbuf[LINE_MAX];
int nldap = 0, nldaps = 0;
int ret = -1;
debug_decl(sudo_ldap_parse_uri, SUDOERS_DEBUG_LDAP)
hostbuf[0] = '\0';
STAILQ_FOREACH(entry, uri_list, entries) {
char *cp, *host, *last, *port, *uri;
buf = strdup(entry->val);
if (buf == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
goto done;
}
for ((uri = strtok_r(buf, " \t", &last)); uri != NULL; (uri = strtok_r(NULL, " \t", &last))) {
if (strncasecmp(uri, "ldap://", 7) == 0) {
nldap++;
host = uri + 7;
} else if (strncasecmp(uri, "ldaps://", 8) == 0) {
nldaps++;
host = uri + 8;
} else {
sudo_warnx(U_("unsupported LDAP uri type: %s"), uri);
goto done;
}
if ((cp = strrchr(host, '/')) != NULL && cp[1] == '\0') {
*cp = '\0';
}
if (hostbuf[0] != '\0')
CHECK_STRLCAT(hostbuf, " ", sizeof(hostbuf));
if (*host == '\0')
host = "localhost";
CHECK_STRLCAT(hostbuf, host, sizeof(hostbuf));
if (nldaps) {
if ((port = strrchr(host, ':')) == NULL ||
!isdigit((unsigned char)port[1]))
CHECK_STRLCAT(hostbuf, ":636", sizeof(hostbuf));
}
}
if (nldaps != 0) {
if (nldap != 0) {
sudo_warnx(U_("unable to mix ldap and ldaps URIs"));
goto done;
}
if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS)
sudo_warnx(U_("starttls not supported when using ldaps"));
ldap_conf.ssl_mode = SUDO_LDAP_SSL;
}
free(buf);
}
buf = NULL;
free(ldap_conf.host);
if ((ldap_conf.host = strdup(hostbuf)) == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
goto done;
}
ret = LDAP_SUCCESS;
done:
free(buf);
debug_return_int(ret);
overflow:
sudo_warnx(U_("internal error, %s overflow"), __func__);
free(buf);
debug_return_int(-1);
}
#endif
static char *
sudo_ldap_decode_secret(const char *secret)
{
unsigned char *result = NULL;
size_t len, reslen;
debug_decl(sudo_ldap_decode_secret, SUDOERS_DEBUG_LDAP)
if (strncasecmp(secret, "base64:", sizeof("base64:") - 1) == 0) {
secret += sizeof("base64:") - 1;
reslen = ((strlen(secret) + 3) / 4 * 3);
result = malloc(reslen + 1);
if (result == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
} else {
len = base64_decode(secret, result, reslen);
if (len == (size_t)-1) {
free(result);
result = NULL;
} else {
result[len] = '\0';
}
}
}
debug_return_str((char *)result);
}
static void
sudo_ldap_read_secret(const char *path)
{
FILE *fp;
char *line = NULL;
size_t linesize = 0;
ssize_t len;
debug_decl(sudo_ldap_read_secret, SUDOERS_DEBUG_LDAP)
if ((fp = fopen(path_ldap_secret, "r")) != NULL) {
len = getdelim(&line, &linesize, '\n', fp);
if (len != -1) {
while (len > 0 && line[len - 1] == '\n')
line[--len] = '\0';
free(ldap_conf.bindpw);
ldap_conf.bindpw = sudo_ldap_decode_secret(line);
if (ldap_conf.bindpw == NULL) {
ldap_conf.bindpw = line;
line = NULL;
}
free(ldap_conf.binddn);
ldap_conf.binddn = ldap_conf.rootbinddn;
ldap_conf.rootbinddn = NULL;
}
fclose(fp);
free(line);
}
debug_return;
}
static bool
sudo_ldap_parse_keyword(const char *keyword, const char *value,
struct ldap_config_table *table)
{
struct ldap_config_table *cur;
const char *errstr;
debug_decl(sudo_ldap_parse_keyword, SUDOERS_DEBUG_LDAP)
for (cur = table; cur->conf_str != NULL; cur++) {
if (strcasecmp(keyword, cur->conf_str) == 0) {
switch (cur->type) {
case CONF_DEREF_VAL:
if (strcasecmp(value, "searching") == 0)
*(int *)(cur->valp) = LDAP_DEREF_SEARCHING;
else if (strcasecmp(value, "finding") == 0)
*(int *)(cur->valp) = LDAP_DEREF_FINDING;
else if (strcasecmp(value, "always") == 0)
*(int *)(cur->valp) = LDAP_DEREF_ALWAYS;
else
*(int *)(cur->valp) = LDAP_DEREF_NEVER;
break;
case CONF_REQCERT_VAL:
#ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
if (strcasecmp(value, "never") == 0)
*(int *)(cur->valp) = LDAP_OPT_X_TLS_NEVER;
else if (strcasecmp(value, "allow") == 0)
*(int *)(cur->valp) = LDAP_OPT_X_TLS_ALLOW;
else if (strcasecmp(value, "try") == 0)
*(int *)(cur->valp) = LDAP_OPT_X_TLS_TRY;
else if (strcasecmp(value, "hard") == 0)
*(int *)(cur->valp) = LDAP_OPT_X_TLS_HARD;
else if (strcasecmp(value, "demand") == 0)
*(int *)(cur->valp) = LDAP_OPT_X_TLS_DEMAND;
#endif
break;
case CONF_BOOL:
*(int *)(cur->valp) = sudo_strtobool(value) == true;
break;
case CONF_INT:
*(int *)(cur->valp) = sudo_strtonum(value, INT_MIN, INT_MAX,
&errstr);
if (errstr != NULL) {
sudo_warnx(U_("%s: %s: %s: %s"),
path_ldap_conf, keyword, value, U_(errstr));
}
break;
case CONF_STR:
{
char *cp = NULL;
free(*(char **)(cur->valp));
if (*value && (cp = strdup(value)) == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return_bool(false);
}
*(char **)(cur->valp) = cp;
break;
}
case CONF_LIST_STR:
{
struct ldap_config_str_list *head;
struct ldap_config_str *str;
size_t len = strlen(value);
if (len > 0) {
head = (struct ldap_config_str_list *)cur->valp;
if ((str = malloc(sizeof(*str) + len)) == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return_bool(false);
}
memcpy(str->val, value, len + 1);
STAILQ_INSERT_TAIL(head, str, entries);
}
}
break;
}
debug_return_bool(true);
}
}
debug_return_bool(false);
}
#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
const char *
sudo_krb5_ccname_path(const char *old_ccname)
{
const char *ccname = old_ccname;
debug_decl(sudo_krb5_ccname_path, SUDOERS_DEBUG_LDAP)
switch (ccname[0]) {
case 'F':
case 'f':
if (strncasecmp(ccname, "FILE:", 5) == 0)
ccname += 5;
break;
case 'W':
case 'w':
if (strncasecmp(ccname, "WRFILE:", 7) == 0)
ccname += 7;
break;
}
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"ccache %s -> %s", old_ccname, ccname);
debug_return_const_str(*ccname == '/' ? ccname : NULL);
}
static bool
sudo_check_krb5_ccname(const char *ccname)
{
int fd = -1;
const char *ccname_path;
debug_decl(sudo_check_krb5_ccname, SUDOERS_DEBUG_LDAP)
ccname_path = sudo_krb5_ccname_path(ccname);
if (ccname_path == NULL) {
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
"unsupported krb5 credential cache path: %s", ccname);
debug_return_bool(false);
}
fd = open(ccname_path, O_RDONLY|O_NONBLOCK, 0);
if (fd == -1) {
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
"unable to open krb5 credential cache: %s", ccname_path);
debug_return_bool(false);
}
close(fd);
sudo_debug_printf(SUDO_DEBUG_INFO,
"using krb5 credential cache: %s", ccname_path);
debug_return_bool(true);
}
#endif
bool
sudo_ldap_read_config(void)
{
char *cp, *keyword, *value, *line = NULL;
struct ldap_config_str *conf_str;
size_t linesize = 0;
FILE *fp;
debug_decl(sudo_ldap_read_config, SUDOERS_DEBUG_LDAP)
ldap_conf.version = 3;
ldap_conf.port = -1;
ldap_conf.tls_checkpeer = -1;
ldap_conf.tls_reqcert = -1;
ldap_conf.timelimit = -1;
ldap_conf.timeout = -1;
ldap_conf.bind_timelimit = -1;
ldap_conf.use_sasl = -1;
ldap_conf.rootuse_sasl = -1;
ldap_conf.deref = -1;
ldap_conf.search_filter = strdup(DEFAULT_SEARCH_FILTER);
ldap_conf.netgroup_search_filter = strdup(DEFAULT_NETGROUP_SEARCH_FILTER);
STAILQ_INIT(&ldap_conf.uri);
STAILQ_INIT(&ldap_conf.base);
STAILQ_INIT(&ldap_conf.netgroup_base);
if (ldap_conf.search_filter == NULL || ldap_conf.netgroup_search_filter == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return_bool(false);
}
if ((fp = fopen(path_ldap_conf, "r")) == NULL)
debug_return_bool(false);
while (sudo_parseln(&line, &linesize, NULL, fp, PARSELN_COMM_BOL|PARSELN_CONT_IGN) != -1) {
if (*line == '\0')
continue;
keyword = cp = line;
while (*cp && !isblank((unsigned char) *cp))
cp++;
if (*cp)
*cp++ = '\0';
while (isblank((unsigned char) *cp))
cp++;
value = cp;
if (!sudo_ldap_parse_keyword(keyword, value, ldap_conf_global))
sudo_ldap_parse_keyword(keyword, value, ldap_conf_conn);
}
free(line);
fclose(fp);
if (!ldap_conf.host) {
ldap_conf.host = strdup("localhost");
if (ldap_conf.host == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return_bool(false);
}
}
DPRINTF1("LDAP Config Summary");
DPRINTF1("===================");
if (!STAILQ_EMPTY(&ldap_conf.uri)) {
STAILQ_FOREACH(conf_str, &ldap_conf.uri, entries) {
DPRINTF1("uri %s", conf_str->val);
}
} else {
DPRINTF1("host %s",
ldap_conf.host ? ldap_conf.host : "(NONE)");
DPRINTF1("port %d", ldap_conf.port);
}
DPRINTF1("ldap_version %d", ldap_conf.version);
if (!STAILQ_EMPTY(&ldap_conf.base)) {
STAILQ_FOREACH(conf_str, &ldap_conf.base, entries) {
DPRINTF1("sudoers_base %s", conf_str->val);
}
} else {
DPRINTF1("sudoers_base %s", "(NONE: LDAP disabled)");
}
if (ldap_conf.search_filter) {
DPRINTF1("search_filter %s", ldap_conf.search_filter);
}
if (!STAILQ_EMPTY(&ldap_conf.netgroup_base)) {
STAILQ_FOREACH(conf_str, &ldap_conf.netgroup_base, entries) {
DPRINTF1("netgroup_base %s", conf_str->val);
}
} else {
DPRINTF1("netgroup_base %s", "(NONE: will use nsswitch)");
}
if (ldap_conf.netgroup_search_filter) {
DPRINTF1("netgroup_search_filter %s", ldap_conf.netgroup_search_filter);
}
DPRINTF1("binddn %s",
ldap_conf.binddn ? ldap_conf.binddn : "(anonymous)");
DPRINTF1("bindpw %s",
ldap_conf.bindpw ? ldap_conf.bindpw : "(anonymous)");
if (ldap_conf.bind_timelimit > 0) {
DPRINTF1("bind_timelimit %d", ldap_conf.bind_timelimit);
}
if (ldap_conf.timelimit > 0) {
DPRINTF1("timelimit %d", ldap_conf.timelimit);
}
if (ldap_conf.deref != -1) {
DPRINTF1("deref %d", ldap_conf.deref);
}
DPRINTF1("ssl %s", ldap_conf.ssl ? ldap_conf.ssl : "(no)");
if (ldap_conf.tls_checkpeer != -1) {
DPRINTF1("tls_checkpeer %s",
ldap_conf.tls_checkpeer ? "(yes)" : "(no)");
}
#ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
if (ldap_conf.tls_reqcert != -1) {
DPRINTF1("tls_reqcert %s",
ldap_conf.tls_reqcert == LDAP_OPT_X_TLS_NEVER ? "hard" :
ldap_conf.tls_reqcert == LDAP_OPT_X_TLS_ALLOW ? "allow" :
ldap_conf.tls_reqcert == LDAP_OPT_X_TLS_TRY ? "try" :
ldap_conf.tls_reqcert == LDAP_OPT_X_TLS_HARD ? "hard" :
ldap_conf.tls_reqcert == LDAP_OPT_X_TLS_DEMAND ? "demand" :
"unknown");
}
#endif
if (ldap_conf.tls_cacertfile != NULL) {
DPRINTF1("tls_cacertfile %s", ldap_conf.tls_cacertfile);
}
if (ldap_conf.tls_cacertdir != NULL) {
DPRINTF1("tls_cacertdir %s", ldap_conf.tls_cacertdir);
}
if (ldap_conf.tls_random_file != NULL) {
DPRINTF1("tls_random_file %s", ldap_conf.tls_random_file);
}
if (ldap_conf.tls_cipher_suite != NULL) {
DPRINTF1("tls_cipher_suite %s", ldap_conf.tls_cipher_suite);
}
if (ldap_conf.tls_certfile != NULL) {
DPRINTF1("tls_certfile %s", ldap_conf.tls_certfile);
}
if (ldap_conf.tls_keyfile != NULL) {
DPRINTF1("tls_keyfile %s", ldap_conf.tls_keyfile);
}
#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
if (ldap_conf.use_sasl != -1) {
if (ldap_conf.sasl_mech == NULL) {
ldap_conf.sasl_mech = strdup("GSSAPI");
if (ldap_conf.sasl_mech == NULL) {
sudo_warnx(U_("%s: %s"), __func__,
U_("unable to allocate memory"));
debug_return_bool(false);
}
}
DPRINTF1("use_sasl %s", ldap_conf.use_sasl ? "yes" : "no");
DPRINTF1("sasl_mech %s", ldap_conf.sasl_mech);
DPRINTF1("sasl_auth_id %s",
ldap_conf.sasl_auth_id ? ldap_conf.sasl_auth_id : "(NONE)");
DPRINTF1("rootuse_sasl %d",
ldap_conf.rootuse_sasl);
DPRINTF1("rootsasl_auth_id %s",
ldap_conf.rootsasl_auth_id ? ldap_conf.rootsasl_auth_id : "(NONE)");
DPRINTF1("sasl_secprops %s",
ldap_conf.sasl_secprops ? ldap_conf.sasl_secprops : "(NONE)");
DPRINTF1("krb5_ccname %s",
ldap_conf.krb5_ccname ? ldap_conf.krb5_ccname : "(NONE)");
}
#endif
DPRINTF1("===================");
if (STAILQ_EMPTY(&ldap_conf.base))
debug_return_bool(false);
if (ldap_conf.bind_timelimit > 0)
ldap_conf.bind_timelimit *= 1000;
if (ldap_conf.ssl != NULL) {
if (strcasecmp(ldap_conf.ssl, "start_tls") == 0)
ldap_conf.ssl_mode = SUDO_LDAP_STARTTLS;
else if (sudo_strtobool(ldap_conf.ssl) == true)
ldap_conf.ssl_mode = SUDO_LDAP_SSL;
}
#if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
if (ldap_conf.tls_checkpeer != -1) {
ldapssl_set_strength(NULL,
ldap_conf.tls_checkpeer ? LDAPSSL_AUTH_CERT : LDAPSSL_AUTH_WEAK);
}
#endif
#ifndef HAVE_LDAP_INITIALIZE
if (!STAILQ_EMPTY(&ldap_conf.uri)) {
struct ldap_config_str *uri;
if (sudo_ldap_parse_uri(&ldap_conf.uri) != LDAP_SUCCESS)
debug_return_bool(false);
while ((uri = STAILQ_FIRST(&ldap_conf.uri)) != NULL) {
STAILQ_REMOVE_HEAD(&ldap_conf.uri, entries);
free(uri);
}
ldap_conf.port = LDAP_PORT;
}
#endif
if (STAILQ_EMPTY(&ldap_conf.uri)) {
if (ldap_conf.port < 0)
ldap_conf.port =
ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT;
#ifdef HAVE_LDAP_CREATE
if (ldap_conf.port != LDAP_PORT) {
if (!sudo_ldap_conf_add_ports())
debug_return_bool(false);
}
#endif
}
if (ldap_conf.search_filter && ldap_conf.search_filter[0] != '(') {
size_t len = strlen(ldap_conf.search_filter);
cp = ldap_conf.search_filter;
ldap_conf.search_filter = malloc(len + 3);
if (ldap_conf.search_filter == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return_bool(false);
}
ldap_conf.search_filter[0] = '(';
memcpy(ldap_conf.search_filter + 1, cp, len);
ldap_conf.search_filter[len + 1] = ')';
ldap_conf.search_filter[len + 2] = '\0';
free(cp);
}
if (ldap_conf.rootbinddn) {
sudo_ldap_read_secret(path_ldap_secret);
} else if (ldap_conf.bindpw) {
cp = sudo_ldap_decode_secret(ldap_conf.bindpw);
if (cp != NULL) {
free(ldap_conf.bindpw);
ldap_conf.bindpw = cp;
}
}
if (ldap_conf.tls_keypw) {
cp = sudo_ldap_decode_secret(ldap_conf.tls_keypw);
if (cp != NULL) {
free(ldap_conf.tls_keypw);
ldap_conf.tls_keypw = cp;
}
}
#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
if (ldap_conf.krb5_ccname != NULL) {
if (!sudo_check_krb5_ccname(ldap_conf.krb5_ccname))
ldap_conf.krb5_ccname = NULL;
}
#endif
debug_return_bool(true);
}
static int
sudo_ldap_set_options_table(LDAP *ld, struct ldap_config_table *table)
{
struct ldap_config_table *cur;
int ival, rc, errors = 0;
char *sval;
debug_decl(sudo_ldap_set_options_table, SUDOERS_DEBUG_LDAP)
for (cur = table; cur->conf_str != NULL; cur++) {
if (cur->opt_val == -1)
continue;
switch (cur->type) {
case CONF_BOOL:
case CONF_INT:
ival = *(int *)(cur->valp);
if (ival >= 0) {
DPRINTF1("ldap_set_option: %s -> %d", cur->conf_str, ival);
rc = ldap_set_option(ld, cur->opt_val, &ival);
if (rc != LDAP_OPT_SUCCESS) {
sudo_warnx("ldap_set_option: %s -> %d: %s",
cur->conf_str, ival, ldap_err2string(rc));
errors++;
}
}
break;
case CONF_STR:
sval = *(char **)(cur->valp);
if (sval != NULL) {
DPRINTF1("ldap_set_option: %s -> %s", cur->conf_str, sval);
rc = ldap_set_option(ld, cur->opt_val, sval);
if (rc != LDAP_OPT_SUCCESS) {
sudo_warnx("ldap_set_option: %s -> %s: %s",
cur->conf_str, sval, ldap_err2string(rc));
errors++;
}
}
break;
}
}
debug_return_int(errors ? -1 : LDAP_SUCCESS);
}
int
sudo_ldap_set_options_global(void)
{
int ret;
debug_decl(sudo_ldap_set_options_global, SUDOERS_DEBUG_LDAP)
#ifdef LBER_OPT_DEBUG_LEVEL
if (ldap_conf.ldap_debug)
ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug);
#endif
ret = sudo_ldap_set_options_table(NULL, ldap_conf_global);
debug_return_int(ret);
}
int
sudo_ldap_set_options_conn(LDAP *ld)
{
int rc;
debug_decl(sudo_ldap_set_options_conn, SUDOERS_DEBUG_LDAP)
rc = sudo_ldap_set_options_table(ld, ldap_conf_conn);
if (rc == -1)
debug_return_int(-1);
#ifdef LDAP_OPT_TIMEOUT
if (ldap_conf.timeout > 0) {
struct timeval tv;
tv.tv_sec = ldap_conf.timeout;
tv.tv_usec = 0;
DPRINTF1("ldap_set_option(LDAP_OPT_TIMEOUT, %d)", ldap_conf.timeout);
rc = ldap_set_option(ld, LDAP_OPT_TIMEOUT, &tv);
if (rc != LDAP_OPT_SUCCESS) {
sudo_warnx("ldap_set_option(TIMEOUT, %d): %s",
ldap_conf.timeout, ldap_err2string(rc));
}
}
#endif
#ifdef LDAP_OPT_NETWORK_TIMEOUT
if (ldap_conf.bind_timelimit > 0) {
struct timeval tv;
tv.tv_sec = ldap_conf.bind_timelimit / 1000;
tv.tv_usec = 0;
DPRINTF1("ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %d)",
ldap_conf.bind_timelimit / 1000);
rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
# if !defined(LDAP_OPT_CONNECT_TIMEOUT) || LDAP_VENDOR_VERSION != 510
if (rc != LDAP_OPT_SUCCESS) {
sudo_warnx("ldap_set_option(NETWORK_TIMEOUT, %d): %s",
ldap_conf.bind_timelimit / 1000, ldap_err2string(rc));
}
# endif
}
#endif
#if defined(LDAP_OPT_X_TLS) && !defined(HAVE_LDAPSSL_INIT)
if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
int val = LDAP_OPT_X_TLS_HARD;
DPRINTF1("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD)");
rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val);
if (rc != LDAP_SUCCESS) {
sudo_warnx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s",
ldap_err2string(rc));
debug_return_int(-1);
}
}
#endif
debug_return_int(LDAP_SUCCESS);
}