~ubuntu-branches/ubuntu/saucy/postfix/saucy

« back to all changes in this revision

Viewing changes to src/global/dict_ldap.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2011-02-22 11:20:43 UTC
  • mfrom: (1.1.27 upstream)
  • Revision ID: james.westby@ubuntu.com-20110222112043-c34ht219w3ybrilr
Tags: 2.8.0-2
* a little more lintian cleanup
* Fix missing format strings in smtp-sink.c

Show diffs side-by-side

added added

removed removed

Lines of Context:
103
103
/* .IP version
104
104
/*      Specifies the LDAP protocol version to use.  Default is version
105
105
/*      \fI2\fR.
 
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.
106
114
/* .IP start_tls
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>
211
220
 
212
221
/* Global library. */
213
222
 
214
223
#include "cfg_parser.h"
215
224
#include "db_common.h"
 
225
#include "mail_conf.h"
 
226
 
 
227
#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
 
228
 /*
 
229
  * SASL headers, for sasl_interact_t. Either SASL v1 or v2 should be fine.
 
230
  */
 
231
#include <sasl.h>
 
232
#endif
216
233
 
217
234
/* Application-specific. */
218
235
 
219
236
#include "dict_ldap.h"
220
237
 
 
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)
 
243
 
 
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,
 
252
#endif
 
253
#endif
 
254
    0, -1,
 
255
};
 
256
 
221
257
typedef struct {
222
258
    LDAP   *conn_ld;
223
259
    int     conn_refcount;
254
290
    int     debuglevel;
255
291
    int     version;
256
292
#ifdef LDAP_API_FEATURE_X_OPENLDAP
 
293
#if defined(USE_LDAP_SASL)
 
294
    int     sasl;
 
295
    char    *sasl_mechs;
 
296
    char    *sasl_realm;
 
297
    char    *sasl_authz;
 
298
    int     sasl_minssf;
 
299
#endif
257
300
    int     ldap_ssl;
258
301
    int     start_tls;
259
302
    int     tls_require_cert;
407
450
    return rc;
408
451
}
409
452
 
 
453
#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
 
454
 /*
 
455
  * Context structure for SASL property callback.
 
456
  */
 
457
typedef struct bind_props {
 
458
    char *authcid;
 
459
    char *passwd;
 
460
    char *realm;
 
461
    char *authzid;
 
462
} bind_props;
 
463
 
 
464
static int
 
465
ldap_b2_interact(LDAP *ld, unsigned flags, void *props, void *inter)
 
466
{
 
467
 
 
468
    sasl_interact_t *in;
 
469
    bind_props *ctx = (bind_props *)props;
 
470
 
 
471
    for (in = inter; in->id != SASL_CB_LIST_END; in++)
 
472
    {
 
473
        in->result = NULL;
 
474
        switch(in->id)
 
475
        {
 
476
        case SASL_CB_GETREALM:
 
477
            in->result = ctx->realm;
 
478
            break;
 
479
        case SASL_CB_AUTHNAME:
 
480
            in->result = ctx->authcid;
 
481
            break;
 
482
        case SASL_CB_USER:
 
483
            in->result = ctx->authzid;
 
484
            break;
 
485
        case SASL_CB_PASS:
 
486
            in->result = ctx->passwd;
 
487
            break;
 
488
        }
 
489
        if (in->result)
 
490
            in->len = strlen(in->result);
 
491
    }
 
492
    return LDAP_SUCCESS;
 
493
}
 
494
#endif
 
495
 
410
496
/* dict_ldap_result - Read and parse LDAP result */
411
497
 
412
498
static int dict_ldap_result(LDAP *ld, int msgid, int timeout, LDAPMessage **res)
427
513
    return LDAP_SUCCESS;
428
514
}
429
515
 
 
516
#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
 
517
 
 
518
/* Asynchronous SASL auth if SASL is enabled */
 
519
 
 
520
static int dict_ldap_bind_sasl(DICT_LDAP *dict_ldap)
 
521
{
 
522
    int     rc;
 
523
    bind_props props;
 
524
    static VSTRING *minssf = 0;
 
525
 
 
526
    if (minssf == 0)
 
527
        minssf = vstring_alloc(12);
 
528
 
 
529
    vstring_sprintf(minssf, "minssf=%d", dict_ldap->sasl_minssf);
 
530
 
 
531
    if ((rc = ldap_set_option(dict_ldap->ld, LDAP_OPT_X_SASL_SECPROPS,
 
532
                               (char *) minssf)) != LDAP_OPT_SUCCESS)
 
533
        return (rc);
 
534
 
 
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;
 
539
 
 
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)
 
544
        return (rc);
 
545
 
 
546
    return (LDAP_SUCCESS);
 
547
}
 
548
#endif
 
549
 
430
550
/* dict_ldap_bind_st - Synchronous simple auth with timeout */
431
551
 
432
552
static int dict_ldap_bind_st(DICT_LDAP *dict_ldap)
746
866
    }
747
867
#endif
748
868
 
 
869
#define DN_LOG_VAL(dict_ldap) \
 
870
        ((dict_ldap)->bind_dn[0] ? (dict_ldap)->bind_dn : "empty or implicit")
749
871
    /*
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.
752
874
     */
753
 
    if (dict_ldap->bind) {
 
875
    if (DICT_LDAP_DO_BIND(dict_ldap)) {
754
876
        if (msg_verbose)
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));
757
879
 
 
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);
 
883
        } else {
 
884
            rc = dict_ldap_bind_st(dict_ldap);
 
885
        }
 
886
#else
758
887
        rc = dict_ldap_bind_st(dict_ldap);
 
888
#endif
759
889
 
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);
765
895
        }
766
896
        if (msg_verbose)
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));
769
899
    }
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);
 
943
#endif
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);
834
970
}
835
971
 
 
972
/* attr_sub_type - Is one of two attributes a sub-type of another */
 
973
 
 
974
static int attrdesc_subtype(const char *a1, const char *a2)
 
975
{
 
976
 
 
977
    /*
 
978
     * RFC 2251 section 4.1.4: LDAP attribute names are case insensitive
 
979
     */
 
980
    while (*a1 && TOLOWER(*a1) == TOLOWER(*a2))
 
981
        ++a1, ++a2;
 
982
 
 
983
    /*
 
984
     * Names equal to end of a1, is a2 equal or a subtype?
 
985
     */
 
986
    if (*a1 == 0 && (*a2 == 0 || *a2 == ';'))
 
987
        return (1);
 
988
 
 
989
    /*
 
990
     * Names equal to end of a2, is a1 a subtype?
 
991
     */
 
992
    if (*a2 == 0 && *a1 == ';')
 
993
        return (-1);
 
994
 
 
995
    /*
 
996
     * Distinct attributes
 
997
     */
 
998
    return (0);
 
999
}
 
1000
 
 
1001
/* url_attrs - attributes we want from LDAP URL */
 
1002
 
 
1003
static char **url_attrs(DICT_LDAP *dict_ldap, LDAPURLDesc * url)
 
1004
{
 
1005
    static ARGV *attrs;
 
1006
    char  **a1;
 
1007
    char  **a2;
 
1008
    int     arel;
 
1009
 
 
1010
    /*
 
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.
 
1014
     * 
 
1015
     * Since we only make use of the various mumble_results attributes, we ask
 
1016
     * only for these, thus making large queries much faster.
 
1017
     * 
 
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.
 
1021
     */
 
1022
    if (url->lud_attrs == 0 || *url->lud_attrs == 0)
 
1023
        return (dict_ldap->result_attributes->argv);
 
1024
 
 
1025
    /*
 
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.
 
1031
     */
 
1032
    if (attrs)
 
1033
        argv_truncate(attrs, 0);
 
1034
    else
 
1035
        attrs = argv_alloc(2);
 
1036
 
 
1037
    /*
 
1038
     * Retrieve only those attributes that are of interest to us.
 
1039
     * 
 
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.
 
1043
     */
 
1044
    for (a1 = url->lud_attrs; *a1; ++a1) {
 
1045
        for (a2 = dict_ldap->result_attributes->argv; *a2; ++a2) {
 
1046
            arel = attrdesc_subtype(*a1, *a2);
 
1047
            if (arel > 0)
 
1048
                argv_add(attrs, *a2, ARGV_END);
 
1049
            else if (arel < 0)
 
1050
                argv_add(attrs, *a1, ARGV_END);
 
1051
        }
 
1052
    }
 
1053
 
 
1054
    return ((attrs->argc > 0) ? attrs->argv : 0);
 
1055
}
 
1056
 
836
1057
/*
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.
852
1073
    LDAPMessage *entry = 0;
853
1074
    BerElement *ber;
854
1075
    char   *attr;
 
1076
    char  **attrs;
855
1077
    struct berval **vals;
856
1078
    int     valcount;
857
1079
    LDAPURLDesc *url;
946
1168
 
947
1169
            /*
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.
951
1173
             * 
952
1174
             * The number of ordinary attributes is "num_attributes".
955
1177
             * index on the "result_attributes" list.
956
1178
             */
957
1179
            for (i = 0; dict_ldap->result_attributes->argv[i]; i++)
958
 
                if (strcasecmp(dict_ldap->result_attributes->argv[i],
959
 
                               attr) == 0)
 
1180
                if (attrdesc_subtype(dict_ldap->result_attributes->argv[i],
 
1181
                                     attr) > 0)
960
1182
                    break;
961
1183
 
962
1184
            /*
968
1190
                    || (!is_leaf &&
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",
974
1196
                                 attr);
975
1197
                } else {
991
1213
                    if (dict_errno != 0)
992
1214
                        continue;
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);
997
1219
                }
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)) {
1003
 
                        if (msg_verbose)
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);
1007
1226
                        if (rc == 0) {
1008
 
                            rc = search_st(dict_ldap->ld, url->lud_dn,
1009
 
                                           url->lud_scope, url->lud_filter,
1010
 
                                         url->lud_attrs, dict_ldap->timeout,
1011
 
                                           &resloop);
 
1227
                            if ((attrs = url_attrs(dict_ldap, url)) != 0) {
 
1228
                                if (msg_verbose)
 
1229
                                    msg_info("%s[%d]: looking up URL %s",
 
1230
                                             myname, recursion,
 
1231
                                             vals[i]->bv_val);
 
1232
                                rc = search_st(dict_ldap->ld, url->lud_dn,
 
1233
                                               url->lud_scope,
 
1234
                                               url->lud_filter,
 
1235
                                               attrs, dict_ldap->timeout,
 
1236
                                               &resloop);
 
1237
                            }
1012
1238
                            ldap_free_urldesc(url);
 
1239
                            if (attrs == 0) {
 
1240
                                if (msg_verbose)
 
1241
                                    msg_info("%s[%d]: skipping URL %s: no "
 
1242
                                             "pertinent attributes", myname,
 
1243
                                             recursion, vals[i]->bv_val);
 
1244
                                continue;
 
1245
                            }
 
1246
                        } else {
 
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;
 
1251
                            break;
1013
1252
                        }
1014
1253
                    } else {
1015
1254
                        if (msg_verbose)
1046
1285
                    if (dict_errno != 0)
1047
1286
                        break;
1048
1287
                }
1049
 
                if (dict_errno != 0)
1050
 
                    continue;
1051
 
                if (msg_verbose)
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"
1098
1335
        }
1099
1336
 
1100
1337
    /*
 
1338
     * Don't frustrate future attempts to make Postfix UTF-8 transparent.
 
1339
     */
 
1340
    if (!valid_utf_8(name, strlen(name))) {
 
1341
        if (msg_verbose)
 
1342
            msg_info("%s: %s: Skipping lookup of non-UTF-8 key '%s'",
 
1343
                     myname, dict_ldap->parser->name, name);
 
1344
        return (0);
 
1345
    }
 
1346
 
 
1347
    /*
1101
1348
     * Optionally fold the key.
1102
1349
     */
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);
 
1590
    }
 
1591
#endif
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);
1359
1613
    char   *server_host;
1360
1614
    char   *scope;
1361
1615
    char   *attr;
 
1616
    char   *bindopt;
1362
1617
    int     tmp;
1363
1618
    int     vendor_version = dict_ldap_vendor_version();
1364
1619
 
1564
1819
    myfree(attr);
1565
1820
 
1566
1821
    /*
1567
 
     * get configured value of "bind"; default to true
 
1822
     * get configured value of "bind"; default to simple bind
1568
1823
     */
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);
 
1829
    myfree(bindopt);
1570
1830
 
1571
1831
    /*
1572
1832
     * get configured value of "bind_dn"; default to ""
1621
1881
                                              "chase_referrals", 0);
1622
1882
 
1623
1883
#ifdef LDAP_API_FEATURE_X_OPENLDAP
 
1884
#if defined(USE_LDAP_SASL)
 
1885
    /*
 
1886
     * SASL options
 
1887
     */
 
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);
 
1897
    } else {
 
1898
        dict_ldap->sasl_mechs = 0;
 
1899
        dict_ldap->sasl_realm = 0;
 
1900
        dict_ldap->sasl_authz = 0;
 
1901
    }
 
1902
#endif
1624
1903
 
1625
1904
    /*
1626
1905
     * TLS options