104
104
/* Specifies the LDAP protocol version to use. Default is version
106
/* .IP "\fBsasl_mechs (empty)\fR"
107
/* Specifies a space-separated list of LDAP SASL Mechanisms.
108
/* .IP "\fBsasl_realm (empty)\fR"
109
/* The realm to use for SASL binds.
110
/* .IP "\fBsasl_authz_id (empty)\fR"
111
/* The SASL Authorization Identity to assert.
112
/* .IP "\fBsasl_minssf (0)\fR"
113
/* The minimum SASL SSF to allow.
107
115
/* Whether or not to issue STARTTLS upon connection to the server.
108
116
/* At this time, STARTTLS and LDAP SSL are only available if the
208
216
#include <dict.h>
209
217
#include <stringops.h>
210
218
#include <binhash.h>
219
#include <name_code.h>
212
221
/* Global library. */
214
223
#include "cfg_parser.h"
215
224
#include "db_common.h"
225
#include "mail_conf.h"
227
#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
229
* SASL headers, for sasl_interact_t. Either SASL v1 or v2 should be fine.
217
234
/* Application-specific. */
219
236
#include "dict_ldap.h"
238
#define DICT_LDAP_BIND_NONE 0
239
#define DICT_LDAP_BIND_SIMPLE 1
240
#define DICT_LDAP_BIND_SASL 2
241
#define DICT_LDAP_DO_BIND(d) ((d)->bind != DICT_LDAP_BIND_NONE)
242
#define DICT_LDAP_DO_SASL(d) ((d)->bind == DICT_LDAP_BIND_SASL)
244
static const NAME_CODE bindopt_table[] = {
245
CONFIG_BOOL_NO, DICT_LDAP_BIND_NONE,
246
"none", DICT_LDAP_BIND_NONE,
247
CONFIG_BOOL_YES, DICT_LDAP_BIND_SIMPLE,
248
"simple", DICT_LDAP_BIND_SIMPLE,
249
#ifdef LDAP_API_FEATURE_X_OPENLDAP
250
#if defined(USE_LDAP_SASL)
251
"sasl", DICT_LDAP_BIND_SASL,
223
259
int conn_refcount;
453
#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
455
* Context structure for SASL property callback.
457
typedef struct bind_props {
465
ldap_b2_interact(LDAP *ld, unsigned flags, void *props, void *inter)
469
bind_props *ctx = (bind_props *)props;
471
for (in = inter; in->id != SASL_CB_LIST_END; in++)
476
case SASL_CB_GETREALM:
477
in->result = ctx->realm;
479
case SASL_CB_AUTHNAME:
480
in->result = ctx->authcid;
483
in->result = ctx->authzid;
486
in->result = ctx->passwd;
490
in->len = strlen(in->result);
410
496
/* dict_ldap_result - Read and parse LDAP result */
412
498
static int dict_ldap_result(LDAP *ld, int msgid, int timeout, LDAPMessage **res)
427
513
return LDAP_SUCCESS;
516
#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
518
/* Asynchronous SASL auth if SASL is enabled */
520
static int dict_ldap_bind_sasl(DICT_LDAP *dict_ldap)
524
static VSTRING *minssf = 0;
527
minssf = vstring_alloc(12);
529
vstring_sprintf(minssf, "minssf=%d", dict_ldap->sasl_minssf);
531
if ((rc = ldap_set_option(dict_ldap->ld, LDAP_OPT_X_SASL_SECPROPS,
532
(char *) minssf)) != LDAP_OPT_SUCCESS)
535
props.authcid = dict_ldap->bind_dn;
536
props.passwd = dict_ldap->bind_pw;
537
props.realm = dict_ldap->sasl_realm;
538
props.authzid = dict_ldap->sasl_authz;
540
if ((rc = ldap_sasl_interactive_bind_s(dict_ldap->ld, NULL,
541
dict_ldap->sasl_mechs, NULL, NULL,
542
LDAP_SASL_QUIET, ldap_b2_interact,
543
&props)) != LDAP_SUCCESS)
546
return (LDAP_SUCCESS);
430
550
/* dict_ldap_bind_st - Synchronous simple auth with timeout */
432
552
static int dict_ldap_bind_st(DICT_LDAP *dict_ldap)
869
#define DN_LOG_VAL(dict_ldap) \
870
((dict_ldap)->bind_dn[0] ? (dict_ldap)->bind_dn : "empty or implicit")
750
872
* If this server requires a bind, do so. Thanks to Sam Tardieu for
751
873
* noticing that the original bind call was broken.
753
if (dict_ldap->bind) {
875
if (DICT_LDAP_DO_BIND(dict_ldap)) {
755
msg_info("%s: Binding to server %s as dn %s",
756
myname, dict_ldap->server_host, dict_ldap->bind_dn);
877
msg_info("%s: Binding to server %s with dn %s",
878
myname, dict_ldap->server_host, DN_LOG_VAL(dict_ldap));
880
#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
881
if (DICT_LDAP_DO_SASL(dict_ldap)) {
882
rc = dict_ldap_bind_sasl(dict_ldap);
884
rc = dict_ldap_bind_st(dict_ldap);
758
887
rc = dict_ldap_bind_st(dict_ldap);
760
890
if (rc != LDAP_SUCCESS) {
761
msg_warn("%s: Unable to bind to server %s as %s: %d (%s)",
762
myname, dict_ldap->server_host, dict_ldap->bind_dn,
891
msg_warn("%s: Unable to bind to server %s with dn %s: %d (%s)",
892
myname, dict_ldap->server_host, DN_LOG_VAL(dict_ldap),
763
893
rc, ldap_err2string(rc));
764
894
DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
767
msg_info("%s: Successful bind to server %s as %s ",
768
myname, dict_ldap->server_host, dict_ldap->bind_dn);
897
msg_info("%s: Successful bind to server %s with dn %s",
898
myname, dict_ldap->server_host, DN_LOG_VAL(dict_ldap));
770
900
/* Save connection handle in shared container */
771
901
DICT_LDAP_CONN(dict_ldap)->conn_ld = dict_ldap->ld;
798
928
ADDSTR(keybuf, dict_ldap->server_host);
799
929
ADDINT(keybuf, dict_ldap->server_port);
800
930
ADDINT(keybuf, dict_ldap->bind);
801
ADDSTR(keybuf, dict_ldap->bind ? dict_ldap->bind_dn : "");
802
ADDSTR(keybuf, dict_ldap->bind ? dict_ldap->bind_pw : "");
931
ADDSTR(keybuf, DICT_LDAP_DO_BIND(dict_ldap) ? dict_ldap->bind_dn : "");
932
ADDSTR(keybuf, DICT_LDAP_DO_BIND(dict_ldap) ? dict_ldap->bind_pw : "");
803
933
ADDINT(keybuf, dict_ldap->dereference);
804
934
ADDINT(keybuf, dict_ldap->chase_referrals);
805
935
ADDINT(keybuf, dict_ldap->debuglevel);
806
936
ADDINT(keybuf, dict_ldap->version);
807
937
#ifdef LDAP_API_FEATURE_X_OPENLDAP
938
#if defined(USE_LDAP_SASL)
939
ADDSTR(keybuf, DICT_LDAP_DO_SASL(dict_ldap) ? dict_ldap->sasl_mechs : "");
940
ADDSTR(keybuf, DICT_LDAP_DO_SASL(dict_ldap) ? dict_ldap->sasl_realm : "");
941
ADDSTR(keybuf, DICT_LDAP_DO_SASL(dict_ldap) ? dict_ldap->sasl_authz : "");
942
ADDINT(keybuf, DICT_LDAP_DO_SASL(dict_ldap) ? dict_ldap->sasl_minssf : 0);
808
944
ADDINT(keybuf, dict_ldap->ldap_ssl);
809
945
ADDINT(keybuf, dict_ldap->start_tls);
810
946
ADDINT(keybuf, sslon ? dict_ldap->tls_require_cert : 0);
833
969
vstring_free(keybuf);
972
/* attr_sub_type - Is one of two attributes a sub-type of another */
974
static int attrdesc_subtype(const char *a1, const char *a2)
978
* RFC 2251 section 4.1.4: LDAP attribute names are case insensitive
980
while (*a1 && TOLOWER(*a1) == TOLOWER(*a2))
984
* Names equal to end of a1, is a2 equal or a subtype?
986
if (*a1 == 0 && (*a2 == 0 || *a2 == ';'))
990
* Names equal to end of a2, is a1 a subtype?
992
if (*a2 == 0 && *a1 == ';')
996
* Distinct attributes
1001
/* url_attrs - attributes we want from LDAP URL */
1003
static char **url_attrs(DICT_LDAP *dict_ldap, LDAPURLDesc * url)
1011
* If the LDAP URI specified no attributes, all entry attributes are
1012
* returned, leading to unnecessarily large LDAP results, particularly
1013
* since dynamic groups are most useful for large groups.
1015
* Since we only make use of the various mumble_results attributes, we ask
1016
* only for these, thus making large queries much faster.
1018
* In one test case, a query returning 75K users took 16 minutes when all
1019
* attributes are returned, and just under 3 minutes with only the
1020
* desired result attribute.
1022
if (url->lud_attrs == 0 || *url->lud_attrs == 0)
1023
return (dict_ldap->result_attributes->argv);
1026
* When the LDAP URI explicitly specifies a set of attributes, we use the
1027
* interection of the URI attributes and our result attributes. This way
1028
* LDAP URIs can hide certain attributes that should not be part of the
1029
* query. There is no point in retrieving attributes not listed in our
1030
* result set, we won't make any use of those.
1033
argv_truncate(attrs, 0);
1035
attrs = argv_alloc(2);
1038
* Retrieve only those attributes that are of interest to us.
1040
* If the URL attribute and the attribute we want differ only in the
1041
* "options" part of the attribute descriptor, select the more specific
1042
* attribute descriptor.
1044
for (a1 = url->lud_attrs; *a1; ++a1) {
1045
for (a2 = dict_ldap->result_attributes->argv; *a2; ++a2) {
1046
arel = attrdesc_subtype(*a1, *a2);
1048
argv_add(attrs, *a2, ARGV_END);
1050
argv_add(attrs, *a1, ARGV_END);
1054
return ((attrs->argc > 0) ? attrs->argv : 0);
837
1058
* dict_ldap_get_values: for each entry returned by a search, get the values
838
1059
* of all its attributes. Recurses to resolve any DN or URL values found.
948
1170
* The "result_attributes" list enumerates all the requested
949
* attributes, first the ordinary result attribtutes and then the
1171
* attributes, first the ordinary result attributes and then the
950
1172
* special result attributes that hold DN or LDAP URL values.
952
1174
* The number of ordinary attributes is "num_attributes".
969
1191
i < dict_ldap->num_terminal + dict_ldap->num_leaf)) {
970
1192
if (msg_verbose)
971
msg_info("%s[%d]: skipping %ld value(s) of %s "
972
"attribute %s", myname, recursion, i,
1193
msg_info("%s[%d]: skipping %d value(s) of %s "
1194
"attribute %s", myname, recursion, valcount,
973
1195
is_terminal ? "non-terminal" : "leaf-only",
991
1213
if (dict_errno != 0)
993
1215
if (msg_verbose)
994
msg_info("%s[%d]: search returned %ld value(s) for"
1216
msg_info("%s[%d]: search returned %d value(s) for"
995
1217
" requested result attribute %s",
996
myname, recursion, i, attr);
1218
myname, recursion, valcount, attr);
998
1220
} else if (recursion < dict_ldap->recursion_limit
999
1221
&& dict_ldap->result_attributes->argv[i]) {
1000
1222
/* Special result attribute */
1001
1223
for (i = 0; i < valcount; i++) {
1002
1224
if (ldap_is_ldap_url(vals[i]->bv_val)) {
1004
msg_info("%s[%d]: looking up URL %s", myname,
1005
recursion, vals[i]->bv_val);
1006
1225
rc = ldap_url_parse(vals[i]->bv_val, &url);
1008
rc = search_st(dict_ldap->ld, url->lud_dn,
1009
url->lud_scope, url->lud_filter,
1010
url->lud_attrs, dict_ldap->timeout,
1227
if ((attrs = url_attrs(dict_ldap, url)) != 0) {
1229
msg_info("%s[%d]: looking up URL %s",
1232
rc = search_st(dict_ldap->ld, url->lud_dn,
1235
attrs, dict_ldap->timeout,
1012
1238
ldap_free_urldesc(url);
1241
msg_info("%s[%d]: skipping URL %s: no "
1242
"pertinent attributes", myname,
1243
recursion, vals[i]->bv_val);
1247
msg_warn("%s[%d]: malformed URL %s: %s(%d)",
1248
myname, recursion, vals[i]->bv_val,
1249
ldap_err2string(rc), rc);
1250
dict_errno = DICT_ERR_RETRY;
1015
1254
if (msg_verbose)
1046
1285
if (dict_errno != 0)
1049
if (dict_errno != 0)
1052
msg_info("%s[%d]: search returned %ld value(s) for"
1288
if (msg_verbose && dict_errno == 0)
1289
msg_info("%s[%d]: search returned %d value(s) for"
1053
1290
" special result attribute %s",
1054
myname, recursion, i, attr);
1291
myname, recursion, valcount, attr);
1055
1292
} else if (recursion >= dict_ldap->recursion_limit
1056
1293
&& dict_ldap->result_attributes->argv[i]) {
1057
1294
msg_warn("%s[%d]: %s: Recursion limit exceeded"
1338
* Don't frustrate future attempts to make Postfix UTF-8 transparent.
1340
if (!valid_utf_8(name, strlen(name))) {
1342
msg_info("%s: %s: Skipping lookup of non-UTF-8 key '%s'",
1343
myname, dict_ldap->parser->name, name);
1101
1348
* Optionally fold the key.
1103
1350
if (dict->flags & DICT_FLAG_FOLD_FIX) {
1335
1582
if (dict_ldap->ctx)
1336
1583
db_common_free_ctx(dict_ldap->ctx);
1337
1584
#ifdef LDAP_API_FEATURE_X_OPENLDAP
1585
#if defined(USE_LDAP_SASL)
1586
if (DICT_LDAP_DO_SASL(dict_ldap)) {
1587
myfree(dict_ldap->sasl_mechs);
1588
myfree(dict_ldap->sasl_realm);
1589
myfree(dict_ldap->sasl_authz);
1338
1592
myfree(dict_ldap->tls_ca_cert_file);
1339
1593
myfree(dict_ldap->tls_ca_cert_dir);
1340
1594
myfree(dict_ldap->tls_cert);
1567
* get configured value of "bind"; default to true
1822
* get configured value of "bind"; default to simple bind
1569
dict_ldap->bind = cfg_get_bool(dict_ldap->parser, "bind", 1);
1824
bindopt = cfg_get_str(dict_ldap->parser, "bind", CONFIG_BOOL_YES, 1, 0);
1825
dict_ldap->bind = name_code(bindopt_table, NAME_CODE_FLAG_NONE, bindopt);
1826
if (dict_ldap->bind < 0)
1827
msg_fatal("%s: unsupported parameter value: %s = %s",
1828
dict_ldap->parser->name, "bind", bindopt);
1572
1832
* get configured value of "bind_dn"; default to ""
1621
1881
"chase_referrals", 0);
1623
1883
#ifdef LDAP_API_FEATURE_X_OPENLDAP
1884
#if defined(USE_LDAP_SASL)
1888
if (DICT_LDAP_DO_SASL(dict_ldap)) {
1889
dict_ldap->sasl_mechs =
1890
cfg_get_str(dict_ldap->parser, "sasl_mechs", "", 0, 0);
1891
dict_ldap->sasl_realm =
1892
cfg_get_str(dict_ldap->parser, "sasl_realm", "", 0, 0);
1893
dict_ldap->sasl_authz =
1894
cfg_get_str(dict_ldap->parser, "sasl_authz_id", "", 0, 0);
1895
dict_ldap->sasl_minssf =
1896
cfg_get_int(dict_ldap->parser, "sasl_minssf", 0, 0, 4096);
1898
dict_ldap->sasl_mechs = 0;
1899
dict_ldap->sasl_realm = 0;
1900
dict_ldap->sasl_authz = 0;