File indexing completed on 2024-12-15 04:50:16

0001 /*
0002   This file is part of libkldap.
0003   SPDX-FileCopyrightText: 2004-2006 Szombathelyi György <gyurco@freemail.hu>
0004 
0005   SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "ldapoperation.h"
0009 #include "kldap_config.h"
0010 
0011 #include "ldap_core_debug.h"
0012 
0013 #include <QElapsedTimer>
0014 
0015 #include <cstdlib>
0016 
0017 // for struct timeval
0018 #if HAVE_SYS_TIME_H
0019 #include <sys/time.h>
0020 #elif defined(_WIN32)
0021 #include <winsock2.h>
0022 #endif
0023 
0024 #include <sasl/sasl.h>
0025 
0026 #if LDAP_FOUND
0027 #if !HAVE_WINLDAP_H
0028 #include <lber.h>
0029 #include <ldap.h>
0030 #else
0031 #include "w32-ldap-help.h"
0032 #endif // HAVE_WINLDAP_H
0033 #endif // LDAP_FOUND
0034 
0035 #include "ldapdefs.h"
0036 
0037 using namespace KLDAPCore;
0038 
0039 #if LDAP_FOUND
0040 static void extractControls(LdapControls &ctrls, LDAPControl **pctrls);
0041 #endif // LDAP_FOUND
0042 
0043 /*
0044    Returns the difference between msecs and elapsed. If msecs is -1,
0045    however, -1 is returned.
0046 */
0047 static int kldap_timeout_value(int msecs, int elapsed)
0048 {
0049     if (msecs == -1) {
0050         return -1;
0051     }
0052 
0053     int timeout = msecs - elapsed;
0054     return timeout < 0 ? 0 : timeout;
0055 }
0056 
0057 class Q_DECL_HIDDEN LdapOperation::LdapOperationPrivate
0058 {
0059 public:
0060     LdapOperationPrivate();
0061     ~LdapOperationPrivate();
0062 #if LDAP_FOUND
0063     int processResult(int rescode, LDAPMessage *msg);
0064     int bind(const QByteArray &creds, SASL_Callback_Proc *saslproc, void *data, bool async);
0065 #endif
0066     LdapControls mClientCtrls, mServerCtrls, mControls;
0067     LdapObject mObject;
0068     QByteArray mExtOid, mExtData;
0069     QByteArray mServerCred;
0070     QString mMatchedDn;
0071     QList<QByteArray> mReferrals;
0072 
0073     LdapConnection *mConnection = nullptr;
0074 };
0075 
0076 LdapOperation::LdapOperation()
0077     : d(new LdapOperationPrivate)
0078 {
0079     d->mConnection = nullptr;
0080 }
0081 
0082 LdapOperation::LdapOperation(LdapConnection &conn)
0083     : d(new LdapOperationPrivate)
0084 {
0085     setConnection(conn);
0086 }
0087 
0088 LdapOperation::~LdapOperation() = default;
0089 
0090 void LdapOperation::setConnection(LdapConnection &conn)
0091 {
0092     d->mConnection = &conn;
0093 }
0094 
0095 LdapConnection &LdapOperation::connection()
0096 {
0097     return *d->mConnection;
0098 }
0099 
0100 void LdapOperation::setClientControls(const LdapControls &ctrls)
0101 {
0102     d->mClientCtrls = ctrls;
0103 }
0104 
0105 void LdapOperation::setServerControls(const LdapControls &ctrls)
0106 {
0107     d->mServerCtrls = ctrls;
0108 }
0109 
0110 LdapControls LdapOperation::clientControls() const
0111 {
0112     return d->mClientCtrls;
0113 }
0114 
0115 LdapControls LdapOperation::serverControls() const
0116 {
0117     return d->mServerCtrls;
0118 }
0119 
0120 LdapObject LdapOperation::object() const
0121 {
0122     return d->mObject;
0123 }
0124 
0125 LdapControls LdapOperation::controls() const
0126 {
0127     return d->mControls;
0128 }
0129 
0130 QByteArray LdapOperation::extendedOid() const
0131 {
0132     return d->mExtOid;
0133 }
0134 
0135 QByteArray LdapOperation::extendedData() const
0136 {
0137     return d->mExtData;
0138 }
0139 
0140 QString LdapOperation::matchedDn() const
0141 {
0142     return d->mMatchedDn;
0143 }
0144 
0145 QList<QByteArray> LdapOperation::referrals() const
0146 {
0147     return d->mReferrals;
0148 }
0149 
0150 QByteArray LdapOperation::serverCred() const
0151 {
0152     return d->mServerCred;
0153 }
0154 
0155 LdapOperation::LdapOperationPrivate::LdapOperationPrivate() = default;
0156 
0157 LdapOperation::LdapOperationPrivate::~LdapOperationPrivate() = default;
0158 
0159 #if LDAP_FOUND
0160 
0161 static int kldap_sasl_interact(sasl_interact_t *interact, LdapOperation::SASL_Data *data)
0162 {
0163     if (data->proc) {
0164         for (; interact->id != SASL_CB_LIST_END; interact++) {
0165             switch (interact->id) {
0166             case SASL_CB_GETREALM:
0167                 data->creds.fields |= LdapOperation::SASL_Realm;
0168                 break;
0169             case SASL_CB_AUTHNAME:
0170                 data->creds.fields |= LdapOperation::SASL_Authname;
0171                 break;
0172             case SASL_CB_PASS:
0173                 data->creds.fields |= LdapOperation::SASL_Password;
0174                 break;
0175             case SASL_CB_USER:
0176                 data->creds.fields |= LdapOperation::SASL_Authzid;
0177                 break;
0178             }
0179         }
0180         int retval;
0181         if ((retval = data->proc(data->creds, data->data))) {
0182             return retval;
0183         }
0184     }
0185 
0186     QString value;
0187 
0188     while (interact->id != SASL_CB_LIST_END) {
0189         value.clear();
0190         switch (interact->id) {
0191         case SASL_CB_GETREALM:
0192             value = data->creds.realm;
0193             qCDebug(LDAP_LOG) << "SASL_REALM=" << value;
0194             break;
0195         case SASL_CB_AUTHNAME:
0196             value = data->creds.authname;
0197             qCDebug(LDAP_LOG) << "SASL_AUTHNAME=" << value;
0198             break;
0199         case SASL_CB_PASS:
0200             value = data->creds.password;
0201             qCDebug(LDAP_LOG) << "SASL_PASSWD=[hidden]";
0202             break;
0203         case SASL_CB_USER:
0204             value = data->creds.authzid;
0205             qCDebug(LDAP_LOG) << "SASL_AUTHZID=" << value;
0206             break;
0207         }
0208         if (value.isEmpty()) {
0209             interact->result = nullptr;
0210             interact->len = 0;
0211         } else {
0212             interact->result = strdup(value.toUtf8().constData());
0213             interact->len = strlen((const char *)interact->result);
0214         }
0215         interact++;
0216     }
0217     return KLDAP_SUCCESS;
0218 }
0219 
0220 int LdapOperation::LdapOperationPrivate::bind(const QByteArray &creds, SASL_Callback_Proc *saslproc, void *data, bool async)
0221 {
0222     Q_ASSERT(mConnection);
0223     LDAP *ld = (LDAP *)mConnection->handle();
0224     LdapServer server;
0225     server = mConnection->server();
0226 
0227     int ret;
0228 
0229     if (server.auth() == LdapServer::SASL) {
0230 #if !HAVE_WINLDAP_H
0231         auto saslconn = (sasl_conn_t *)mConnection->saslHandle();
0232         sasl_interact_t *client_interact = nullptr;
0233         const char *out = nullptr;
0234         uint outlen;
0235         const char *mechusing = nullptr;
0236         struct berval ccred;
0237         struct berval *scred;
0238         int saslresult;
0239         QByteArray sdata = creds;
0240 
0241         QString mech = server.mech();
0242         if (mech.isEmpty()) {
0243             mech = QStringLiteral("DIGEST-MD5");
0244         }
0245 
0246         SASL_Data sasldata;
0247         sasldata.proc = saslproc;
0248         sasldata.data = data;
0249         sasldata.creds.fields = 0;
0250         sasldata.creds.realm = server.realm();
0251         sasldata.creds.authname = server.user();
0252         sasldata.creds.authzid = server.bindDn();
0253         sasldata.creds.password = server.password();
0254 
0255         do {
0256             if (sdata.isEmpty()) {
0257                 do {
0258                     saslresult = sasl_client_start(saslconn, mech.toLatin1().constData(), &client_interact, &out, &outlen, &mechusing);
0259 
0260                     if (saslresult == SASL_INTERACT) {
0261                         if (kldap_sasl_interact(client_interact, &sasldata) != KLDAP_SUCCESS) {
0262                             return KLDAP_SASL_ERROR;
0263                         }
0264                     }
0265                     qCDebug(LDAP_LOG) << "sasl_client_start mech: " << mechusing << " outlen " << outlen << " result: " << saslresult;
0266                 } while (saslresult == SASL_INTERACT);
0267                 if (saslresult != SASL_CONTINUE && saslresult != SASL_OK) {
0268                     return KLDAP_SASL_ERROR;
0269                 }
0270             } else {
0271                 qCDebug(LDAP_LOG) << "sasl_client_step";
0272                 do {
0273                     saslresult = sasl_client_step(saslconn, sdata.data(), sdata.size(), &client_interact, &out, &outlen);
0274                     if (saslresult == SASL_INTERACT) {
0275                         if (kldap_sasl_interact(client_interact, &sasldata) != KLDAP_SUCCESS) {
0276                             return KLDAP_SASL_ERROR;
0277                         }
0278                     }
0279                 } while (saslresult == SASL_INTERACT);
0280                 qCDebug(LDAP_LOG) << "sasl_client_step result" << saslresult;
0281                 if (saslresult != SASL_CONTINUE && saslresult != SASL_OK) {
0282                     return KLDAP_SASL_ERROR;
0283                 }
0284             }
0285 
0286             ccred.bv_val = (char *)out;
0287             ccred.bv_len = outlen;
0288 
0289             if (async) {
0290                 qCDebug(LDAP_LOG) << "ldap_sasl_bind";
0291                 int msgid;
0292                 ret = ldap_sasl_bind(ld, server.bindDn().toUtf8().constData(), mech.toLatin1().constData(), &ccred, nullptr, nullptr, &msgid);
0293                 if (ret == 0) {
0294                     ret = msgid;
0295                 }
0296                 qCDebug(LDAP_LOG) << "ldap_sasl_bind msgid" << ret;
0297             } else {
0298                 qCDebug(LDAP_LOG) << "ldap_sasl_bind_s";
0299                 ret = ldap_sasl_bind_s(ld, server.bindDn().toUtf8().constData(), mech.toLatin1().constData(), &ccred, nullptr, nullptr, &scred);
0300                 qCDebug(LDAP_LOG) << "ldap_sasl_bind_s ret" << ret;
0301                 if (scred) {
0302                     sdata = QByteArray(scred->bv_val, scred->bv_len);
0303                 } else {
0304                     sdata = QByteArray();
0305                 }
0306             }
0307         } while (!async && ret == KLDAP_SASL_BIND_IN_PROGRESS);
0308 #else
0309         qCritical() << "SASL authentication is not available "
0310                     << "(re-compile kldap with cyrus-sasl and OpenLDAP development).";
0311         return KLDAP_SASL_ERROR;
0312 #endif
0313     } else { // simple auth
0314         QByteArray bindname;
0315         QByteArray pass;
0316         struct berval ccred;
0317         if (server.auth() == LdapServer::Simple) {
0318             bindname = server.bindDn().toUtf8();
0319             pass = server.password().toUtf8();
0320         }
0321         ccred.bv_val = pass.data();
0322         ccred.bv_len = pass.size();
0323         qCDebug(LDAP_LOG) << "binding to server, bindname: " << bindname << " password: *****";
0324 
0325         if (async) {
0326             qCDebug(LDAP_LOG) << "ldap_sasl_bind (simple)";
0327 #if !HAVE_WINLDAP_H
0328             int msgid = 0;
0329             ret = ldap_sasl_bind(ld, bindname.data(), nullptr, &ccred, nullptr, nullptr, &msgid);
0330             if (ret == 0) {
0331                 ret = msgid;
0332             }
0333 #else
0334             ret = ldap_simple_bind(ld, bindname.data(), pass.data());
0335 #endif
0336         } else {
0337             qCDebug(LDAP_LOG) << "ldap_sasl_bind_s (simple)";
0338 #if !HAVE_WINLDAP_H
0339             ret = ldap_sasl_bind_s(ld, bindname.data(), nullptr, &ccred, nullptr, nullptr, nullptr);
0340 #else
0341             ret = ldap_simple_bind_s(ld, bindname.data(), pass.data());
0342 #endif
0343         }
0344     }
0345     return ret;
0346 }
0347 
0348 int LdapOperation::LdapOperationPrivate::processResult(int rescode, LDAPMessage *msg)
0349 {
0350     // qCDebug(LDAP_LOG);
0351     int retval;
0352     LDAP *ld = (LDAP *)mConnection->handle();
0353 
0354     qCDebug(LDAP_LOG) << "rescode: " << rescode;
0355     switch (rescode) {
0356     case RES_SEARCH_ENTRY: {
0357         // qCDebug(LDAP_LOG) << "Found search entry";
0358         mObject.clear();
0359         LdapAttrMap attrs;
0360         char *name;
0361         struct berval **bvals;
0362         BerElement *entry;
0363         LdapAttrValue values;
0364 
0365         char *dn = ldap_get_dn(ld, msg);
0366         mObject.setDn(QString::fromUtf8(dn));
0367         ldap_memfree(dn);
0368 
0369         // iterate over the attributes
0370         name = ldap_first_attribute(ld, msg, &entry);
0371         while (name != nullptr) {
0372             // print the values
0373             bvals = ldap_get_values_len(ld, msg, name);
0374             if (bvals) {
0375                 for (int i = 0; bvals[i] != nullptr; i++) {
0376                     char *val = bvals[i]->bv_val;
0377                     unsigned long len = bvals[i]->bv_len;
0378                     values.append(QByteArray(val, len));
0379                 }
0380                 ldap_value_free_len(bvals);
0381             }
0382             attrs[QString::fromLatin1(name)] = values;
0383             values.clear();
0384             ldap_memfree(name);
0385 
0386             // next attribute
0387             name = ldap_next_attribute(ld, msg, entry);
0388         }
0389         ber_free(entry, 0);
0390         mObject.setAttributes(attrs);
0391         break;
0392     }
0393     case RES_SEARCH_REFERENCE:
0394         // Will only get this if following references is disabled. ignore it
0395         rescode = 0;
0396         break;
0397     case RES_EXTENDED: {
0398         char *retoid;
0399         struct berval *retdata;
0400         retval = ldap_parse_extended_result(ld, msg, &retoid, &retdata, 0);
0401         if (retval != KLDAP_SUCCESS) {
0402             ldap_msgfree(msg);
0403             return -1;
0404         }
0405         mExtOid = retoid ? QByteArray(retoid) : QByteArray();
0406         mExtData = retdata ? QByteArray(retdata->bv_val, retdata->bv_len) : QByteArray();
0407         ldap_memfree(retoid);
0408         ber_bvfree(retdata);
0409         break;
0410     }
0411     case RES_BIND: {
0412         struct berval *servercred = nullptr;
0413 #if !HAVE_WINLDAP_H
0414         // FIXME: Error handling Winldap does not have ldap_parse_sasl_bind_result
0415         retval = ldap_parse_sasl_bind_result(ld, msg, &servercred, 0);
0416 #else
0417         retval = KLDAP_SUCCESS;
0418 #endif
0419         if (retval != KLDAP_SUCCESS && retval != KLDAP_SASL_BIND_IN_PROGRESS) {
0420             qCDebug(LDAP_LOG) << "RES_BIND error: " << retval;
0421             ldap_msgfree(msg);
0422             return -1;
0423         }
0424         qCDebug(LDAP_LOG) << "RES_BIND rescode" << rescode << "retval:" << retval;
0425         if (servercred) {
0426             mServerCred = QByteArray(servercred->bv_val, servercred->bv_len);
0427             ber_bvfree(servercred);
0428         } else {
0429             mServerCred = QByteArray();
0430         }
0431         break;
0432     }
0433     default: {
0434         LDAPControl **serverctrls = nullptr;
0435         char *matcheddn = nullptr;
0436         char *errmsg = nullptr;
0437         char **referralsp;
0438         int errcodep;
0439         retval = ldap_parse_result(ld, msg, &errcodep, &matcheddn, &errmsg, &referralsp, &serverctrls, 0);
0440         qCDebug(LDAP_LOG) << "rescode" << rescode << "retval:" << retval << "matcheddn:" << matcheddn << "errcode:" << errcodep << "errmsg:" << errmsg;
0441         if (retval != KLDAP_SUCCESS) {
0442             ldap_msgfree(msg);
0443             return -1;
0444         }
0445         mControls.clear();
0446         if (serverctrls) {
0447             extractControls(mControls, serverctrls);
0448             ldap_controls_free(serverctrls);
0449         }
0450         mReferrals.clear();
0451         if (referralsp) {
0452             char **tmp = referralsp;
0453             while (*tmp) {
0454                 mReferrals.append(QByteArray(*tmp));
0455                 ldap_memfree(*tmp);
0456                 tmp++;
0457             }
0458             ldap_memfree((char *)referralsp);
0459         }
0460         mMatchedDn.clear();
0461         if (matcheddn) {
0462             mMatchedDn = QString::fromUtf8(matcheddn);
0463             ldap_memfree(matcheddn);
0464         }
0465         if (errmsg) {
0466             ldap_memfree(errmsg);
0467         }
0468     }
0469     }
0470 
0471     ldap_msgfree(msg);
0472 
0473     return rescode;
0474 }
0475 
0476 static void addModOp(LDAPMod ***pmods, int mod_type, const QString &attr, const QByteArray *value = nullptr)
0477 {
0478     //  qCDebug(LDAP_LOG) << "type:" << mod_type << "attr:" << attr <<
0479     //    "value:" << QString::fromUtf8(value,value.size()) <<
0480     //    "size:" << value.size();
0481     LDAPMod **mods;
0482 
0483     mods = *pmods;
0484 
0485     uint i = 0;
0486 
0487     if (mods == nullptr) {
0488         mods = (LDAPMod **)malloc(2 * sizeof(LDAPMod *));
0489         mods[0] = (LDAPMod *)malloc(sizeof(LDAPMod));
0490         mods[1] = nullptr;
0491         memset(mods[0], 0, sizeof(LDAPMod));
0492     } else {
0493         while (mods[i] != nullptr && (strcmp(attr.toUtf8().constData(), mods[i]->mod_type) != 0 || (mods[i]->mod_op & ~LDAP_MOD_BVALUES) != mod_type)) {
0494             i++;
0495         }
0496 
0497         if (mods[i] == nullptr) {
0498             mods = (LDAPMod **)realloc(mods, (i + 2) * sizeof(LDAPMod *));
0499             if (mods == nullptr) {
0500                 qCritical() << "addModOp: realloc";
0501                 return;
0502             }
0503             mods[i + 1] = nullptr;
0504             mods[i] = (LDAPMod *)malloc(sizeof(LDAPMod));
0505             memset(mods[i], 0, sizeof(LDAPMod));
0506         }
0507     }
0508 
0509     mods[i]->mod_op = mod_type | LDAP_MOD_BVALUES;
0510     if (mods[i]->mod_type == nullptr) {
0511         mods[i]->mod_type = strdup(attr.toUtf8().constData());
0512     }
0513 
0514     *pmods = mods;
0515 
0516     if (value == nullptr) {
0517         return;
0518     }
0519 
0520     int vallen = value->size();
0521     BerValue *berval;
0522     berval = (BerValue *)malloc(sizeof(BerValue));
0523     berval->bv_len = vallen;
0524     if (vallen > 0) {
0525         berval->bv_val = (char *)malloc(vallen);
0526         memcpy(berval->bv_val, value->data(), vallen);
0527     } else {
0528         berval->bv_val = nullptr;
0529     }
0530 
0531     if (mods[i]->mod_vals.modv_bvals == nullptr) {
0532         mods[i]->mod_vals.modv_bvals = (BerValue **)malloc(sizeof(BerValue *) * 2);
0533         mods[i]->mod_vals.modv_bvals[0] = berval;
0534         mods[i]->mod_vals.modv_bvals[1] = nullptr;
0535         //    qCDebug(LDAP_LOG) << "new bervalue struct" << attr << value;
0536     } else {
0537         uint j = 0;
0538         while (mods[i]->mod_vals.modv_bvals[j] != nullptr) {
0539             j++;
0540         }
0541         mods[i]->mod_vals.modv_bvals = (BerValue **)realloc(mods[i]->mod_vals.modv_bvals, (j + 2) * sizeof(BerValue *));
0542         if (mods[i]->mod_vals.modv_bvals == nullptr) {
0543             qCritical() << "addModOp: realloc";
0544             free(berval);
0545             return;
0546         }
0547         mods[i]->mod_vals.modv_bvals[j] = berval;
0548         mods[i]->mod_vals.modv_bvals[j + 1] = nullptr;
0549         qCDebug(LDAP_LOG) << j << ". new bervalue";
0550     }
0551 }
0552 
0553 static void addControlOp(LDAPControl ***pctrls, const QString &oid, const QByteArray &value, bool critical)
0554 {
0555     LDAPControl **ctrls;
0556     auto ctrl = (LDAPControl *)malloc(sizeof(LDAPControl));
0557 
0558     ctrls = *pctrls;
0559 
0560     qCDebug(LDAP_LOG) << "oid:'" << oid << "' val: '" << value << "'";
0561     int vallen = value.size();
0562     ctrl->ldctl_value.bv_len = vallen;
0563     if (vallen) {
0564         ctrl->ldctl_value.bv_val = (char *)malloc(vallen);
0565         memcpy(ctrl->ldctl_value.bv_val, value.data(), vallen);
0566     } else {
0567         ctrl->ldctl_value.bv_val = nullptr;
0568     }
0569     ctrl->ldctl_iscritical = critical;
0570     ctrl->ldctl_oid = strdup(oid.toUtf8().constData());
0571 
0572     uint i = 0;
0573 
0574     if (ctrls == nullptr) {
0575         ctrls = (LDAPControl **)malloc(2 * sizeof(LDAPControl *));
0576         ctrls[0] = nullptr;
0577         ctrls[1] = nullptr;
0578     } else {
0579         while (ctrls[i] != nullptr) {
0580             i++;
0581         }
0582         ctrls[i + 1] = nullptr;
0583         ctrls = (LDAPControl **)realloc(ctrls, (i + 2) * sizeof(LDAPControl *));
0584     }
0585     ctrls[i] = ctrl;
0586     *pctrls = ctrls;
0587 }
0588 
0589 static void createControls(LDAPControl ***pctrls, const LdapControls &ctrls)
0590 {
0591     for (int i = 0; i < ctrls.count(); ++i) {
0592         addControlOp(pctrls, ctrls[i].oid(), ctrls[i].value(), ctrls[i].critical());
0593     }
0594 }
0595 
0596 static void extractControls(LdapControls &ctrls, LDAPControl **pctrls)
0597 {
0598     LdapControl control;
0599     int i = 0;
0600 
0601     while (pctrls[i]) {
0602         LDAPControl *ctrl = pctrls[i];
0603         control.setOid(QString::fromUtf8(ctrl->ldctl_oid));
0604         control.setValue(QByteArray(ctrl->ldctl_value.bv_val, ctrl->ldctl_value.bv_len));
0605         control.setCritical(ctrl->ldctl_iscritical);
0606         ctrls.append(control);
0607         i++;
0608     }
0609 }
0610 
0611 int LdapOperation::bind(const QByteArray &creds, SASL_Callback_Proc *saslproc, void *data)
0612 {
0613     return d->bind(creds, saslproc, data, true);
0614 }
0615 
0616 int LdapOperation::bind_s(SASL_Callback_Proc *saslproc, void *data)
0617 {
0618     return d->bind(QByteArray(), saslproc, data, false);
0619 }
0620 
0621 int LdapOperation::search(const LdapDN &base, LdapUrl::Scope scope, const QString &filter, const QStringList &attributes)
0622 {
0623     Q_ASSERT(d->mConnection);
0624     LDAP *ld = (LDAP *)d->mConnection->handle();
0625 
0626     char **attrs = nullptr;
0627     int msgid;
0628 
0629     LDAPControl **serverctrls = nullptr;
0630     LDAPControl **clientctrls = nullptr;
0631     createControls(&serverctrls, d->mServerCtrls);
0632     createControls(&serverctrls, d->mClientCtrls);
0633 
0634     int count = attributes.count();
0635     if (count > 0) {
0636         attrs = static_cast<char **>(malloc((count + 1) * sizeof(char *)));
0637         for (int i = 0; i < count; i++) {
0638             attrs[i] = strdup(attributes.at(i).toUtf8().constData());
0639         }
0640         attrs[count] = nullptr;
0641     }
0642 
0643     int lscope = LDAP_SCOPE_BASE;
0644     switch (scope) {
0645     case LdapUrl::Base:
0646         lscope = LDAP_SCOPE_BASE;
0647         break;
0648     case LdapUrl::One:
0649         lscope = LDAP_SCOPE_ONELEVEL;
0650         break;
0651     case LdapUrl::Sub:
0652         lscope = LDAP_SCOPE_SUBTREE;
0653         break;
0654     }
0655 
0656     qCDebug(LDAP_LOG) << "asyncSearch() base=\"" << base.toString() << "\" scope=" << (int)scope << "filter=\"" << filter << "\" attrs=" << attributes;
0657     int retval = ldap_search_ext(ld,
0658                                  base.toString().toUtf8().data(),
0659                                  lscope,
0660                                  filter.isEmpty() ? QByteArray("objectClass=*").data() : filter.toUtf8().data(),
0661                                  attrs,
0662                                  0,
0663                                  serverctrls,
0664                                  clientctrls,
0665                                  nullptr,
0666                                  d->mConnection->sizeLimit(),
0667                                  &msgid);
0668 
0669     ldap_controls_free(serverctrls);
0670     ldap_controls_free(clientctrls);
0671 
0672     // free the attributes list again
0673     if (count > 0) {
0674         for (int i = 0; i < count; i++) {
0675             free(attrs[i]);
0676         }
0677         free(attrs);
0678     }
0679 
0680     if (retval == 0) {
0681         retval = msgid;
0682     }
0683     return retval;
0684 }
0685 
0686 int LdapOperation::add(const LdapObject &object)
0687 {
0688     Q_ASSERT(d->mConnection);
0689     LDAP *ld = (LDAP *)d->mConnection->handle();
0690 
0691     int msgid;
0692     LDAPMod **lmod = nullptr;
0693 
0694     LDAPControl **serverctrls = nullptr;
0695     LDAPControl **clientctrls = nullptr;
0696     createControls(&serverctrls, d->mServerCtrls);
0697     createControls(&serverctrls, d->mClientCtrls);
0698 
0699     for (LdapAttrMap::ConstIterator it = object.attributes().begin(); it != object.attributes().end(); ++it) {
0700         QString attr = it.key();
0701         for (LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2) {
0702             addModOp(&lmod, 0, attr, &(*it2));
0703         }
0704     }
0705 
0706     int retval = ldap_add_ext(ld, object.dn().toString().toUtf8().data(), lmod, serverctrls, clientctrls, &msgid);
0707 
0708     ldap_controls_free(serverctrls);
0709     ldap_controls_free(clientctrls);
0710     ldap_mods_free(lmod, 1);
0711     if (retval == 0) {
0712         retval = msgid;
0713     }
0714     return retval;
0715 }
0716 
0717 int LdapOperation::add_s(const LdapObject &object)
0718 {
0719     Q_ASSERT(d->mConnection);
0720     LDAP *ld = (LDAP *)d->mConnection->handle();
0721 
0722     LDAPMod **lmod = nullptr;
0723 
0724     LDAPControl **serverctrls = nullptr;
0725     LDAPControl **clientctrls = nullptr;
0726     createControls(&serverctrls, d->mServerCtrls);
0727     createControls(&serverctrls, d->mClientCtrls);
0728 
0729     for (LdapAttrMap::ConstIterator it = object.attributes().begin(); it != object.attributes().end(); ++it) {
0730         QString attr = it.key();
0731         for (LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2) {
0732             addModOp(&lmod, 0, attr, &(*it2));
0733         }
0734     }
0735 
0736     int retval = ldap_add_ext_s(ld, object.dn().toString().toUtf8().data(), lmod, serverctrls, clientctrls);
0737 
0738     ldap_controls_free(serverctrls);
0739     ldap_controls_free(clientctrls);
0740     ldap_mods_free(lmod, 1);
0741     return retval;
0742 }
0743 
0744 int LdapOperation::add(const LdapDN &dn, const ModOps &ops)
0745 {
0746     Q_ASSERT(d->mConnection);
0747     LDAP *ld = (LDAP *)d->mConnection->handle();
0748 
0749     int msgid;
0750     LDAPMod **lmod = nullptr;
0751 
0752     LDAPControl **serverctrls = nullptr;
0753     LDAPControl **clientctrls = nullptr;
0754     createControls(&serverctrls, d->mServerCtrls);
0755     createControls(&serverctrls, d->mClientCtrls);
0756 
0757     for (int i = 0; i < ops.count(); ++i) {
0758         for (int j = 0; j < ops[i].values.count(); ++j) {
0759             addModOp(&lmod, 0, ops[i].attr, &ops[i].values[j]);
0760         }
0761     }
0762 
0763     int retval = ldap_add_ext(ld, dn.toString().toUtf8().data(), lmod, serverctrls, clientctrls, &msgid);
0764 
0765     ldap_controls_free(serverctrls);
0766     ldap_controls_free(clientctrls);
0767     ldap_mods_free(lmod, 1);
0768     if (retval == 0) {
0769         retval = msgid;
0770     }
0771     return retval;
0772 }
0773 
0774 int LdapOperation::add_s(const LdapDN &dn, const ModOps &ops)
0775 {
0776     Q_ASSERT(d->mConnection);
0777     LDAP *ld = (LDAP *)d->mConnection->handle();
0778 
0779     LDAPMod **lmod = nullptr;
0780 
0781     LDAPControl **serverctrls = nullptr;
0782     LDAPControl **clientctrls = nullptr;
0783     createControls(&serverctrls, d->mServerCtrls);
0784     createControls(&serverctrls, d->mClientCtrls);
0785 
0786     for (int i = 0; i < ops.count(); ++i) {
0787         for (int j = 0; j < ops[i].values.count(); ++j) {
0788             addModOp(&lmod, 0, ops[i].attr, &ops[i].values[j]);
0789         }
0790     }
0791     qCDebug(LDAP_LOG) << dn.toString();
0792     int retval = ldap_add_ext_s(ld, dn.toString().toUtf8().data(), lmod, serverctrls, clientctrls);
0793 
0794     ldap_controls_free(serverctrls);
0795     ldap_controls_free(clientctrls);
0796     ldap_mods_free(lmod, 1);
0797     return retval;
0798 }
0799 
0800 int LdapOperation::rename(const LdapDN &dn, const QString &newRdn, const QString &newSuperior, bool deleteold)
0801 {
0802     Q_ASSERT(d->mConnection);
0803     LDAP *ld = (LDAP *)d->mConnection->handle();
0804 
0805     int msgid;
0806 
0807     LDAPControl **serverctrls = nullptr;
0808     LDAPControl **clientctrls = nullptr;
0809     createControls(&serverctrls, d->mServerCtrls);
0810     createControls(&serverctrls, d->mClientCtrls);
0811 
0812     int retval = ldap_rename(ld,
0813                              dn.toString().toUtf8().data(),
0814                              newRdn.toUtf8().data(),
0815                              newSuperior.isEmpty() ? (char *)nullptr : newSuperior.toUtf8().data(),
0816                              deleteold,
0817                              serverctrls,
0818                              clientctrls,
0819                              &msgid);
0820 
0821     ldap_controls_free(serverctrls);
0822     ldap_controls_free(clientctrls);
0823 
0824     if (retval == 0) {
0825         retval = msgid;
0826     }
0827     return retval;
0828 }
0829 
0830 int LdapOperation::rename_s(const LdapDN &dn, const QString &newRdn, const QString &newSuperior, bool deleteold)
0831 {
0832     Q_ASSERT(d->mConnection);
0833     LDAP *ld = (LDAP *)d->mConnection->handle();
0834 
0835     LDAPControl **serverctrls = nullptr;
0836     LDAPControl **clientctrls = nullptr;
0837     createControls(&serverctrls, d->mServerCtrls);
0838     createControls(&serverctrls, d->mClientCtrls);
0839 
0840     int retval = ldap_rename_s(ld,
0841                                dn.toString().toUtf8().data(),
0842                                newRdn.toUtf8().data(),
0843                                newSuperior.isEmpty() ? (char *)nullptr : newSuperior.toUtf8().data(),
0844                                deleteold,
0845                                serverctrls,
0846                                clientctrls);
0847 
0848     ldap_controls_free(serverctrls);
0849     ldap_controls_free(clientctrls);
0850 
0851     return retval;
0852 }
0853 
0854 int LdapOperation::del(const LdapDN &dn)
0855 {
0856     Q_ASSERT(d->mConnection);
0857     LDAP *ld = (LDAP *)d->mConnection->handle();
0858 
0859     int msgid;
0860 
0861     LDAPControl **serverctrls = nullptr;
0862     LDAPControl **clientctrls = nullptr;
0863     createControls(&serverctrls, d->mServerCtrls);
0864     createControls(&serverctrls, d->mClientCtrls);
0865 
0866     int retval = ldap_delete_ext(ld, dn.toString().toUtf8().data(), serverctrls, clientctrls, &msgid);
0867 
0868     ldap_controls_free(serverctrls);
0869     ldap_controls_free(clientctrls);
0870 
0871     if (retval == 0) {
0872         retval = msgid;
0873     }
0874     return retval;
0875 }
0876 
0877 int LdapOperation::del_s(const LdapDN &dn)
0878 {
0879     Q_ASSERT(d->mConnection);
0880     LDAP *ld = (LDAP *)d->mConnection->handle();
0881 
0882     LDAPControl **serverctrls = nullptr;
0883     LDAPControl **clientctrls = nullptr;
0884     createControls(&serverctrls, d->mServerCtrls);
0885     createControls(&serverctrls, d->mClientCtrls);
0886 
0887     int retval = ldap_delete_ext_s(ld, dn.toString().toUtf8().data(), serverctrls, clientctrls);
0888 
0889     ldap_controls_free(serverctrls);
0890     ldap_controls_free(clientctrls);
0891 
0892     return retval;
0893 }
0894 
0895 int LdapOperation::modify(const LdapDN &dn, const ModOps &ops)
0896 {
0897     Q_ASSERT(d->mConnection);
0898     LDAP *ld = (LDAP *)d->mConnection->handle();
0899 
0900     int msgid;
0901     LDAPMod **lmod = nullptr;
0902 
0903     LDAPControl **serverctrls = nullptr;
0904     LDAPControl **clientctrls = nullptr;
0905     createControls(&serverctrls, d->mServerCtrls);
0906     createControls(&serverctrls, d->mClientCtrls);
0907 
0908     for (int i = 0; i < ops.count(); ++i) {
0909         int mtype = 0;
0910         switch (ops[i].type) {
0911         case Mod_None:
0912             mtype = 0;
0913             break;
0914         case Mod_Add:
0915             mtype = LDAP_MOD_ADD;
0916             break;
0917         case Mod_Replace:
0918             mtype = LDAP_MOD_REPLACE;
0919             break;
0920         case Mod_Del:
0921             mtype = LDAP_MOD_DELETE;
0922             break;
0923         }
0924         addModOp(&lmod, mtype, ops[i].attr, nullptr);
0925         for (int j = 0; j < ops[i].values.count(); ++j) {
0926             addModOp(&lmod, mtype, ops[i].attr, &ops[i].values[j]);
0927         }
0928     }
0929 
0930     int retval = ldap_modify_ext(ld, dn.toString().toUtf8().data(), lmod, serverctrls, clientctrls, &msgid);
0931 
0932     ldap_controls_free(serverctrls);
0933     ldap_controls_free(clientctrls);
0934     ldap_mods_free(lmod, 1);
0935     if (retval == 0) {
0936         retval = msgid;
0937     }
0938     return retval;
0939 }
0940 
0941 int LdapOperation::modify_s(const LdapDN &dn, const ModOps &ops)
0942 {
0943     Q_ASSERT(d->mConnection);
0944     LDAP *ld = (LDAP *)d->mConnection->handle();
0945 
0946     LDAPMod **lmod = nullptr;
0947 
0948     LDAPControl **serverctrls = nullptr;
0949     LDAPControl **clientctrls = nullptr;
0950     createControls(&serverctrls, d->mServerCtrls);
0951     createControls(&serverctrls, d->mClientCtrls);
0952 
0953     for (int i = 0; i < ops.count(); ++i) {
0954         int mtype = 0;
0955         switch (ops[i].type) {
0956         case Mod_None:
0957             mtype = 0;
0958             break;
0959         case Mod_Add:
0960             mtype = LDAP_MOD_ADD;
0961             break;
0962         case Mod_Replace:
0963             mtype = LDAP_MOD_REPLACE;
0964             break;
0965         case Mod_Del:
0966             mtype = LDAP_MOD_DELETE;
0967             break;
0968         }
0969         addModOp(&lmod, mtype, ops[i].attr, nullptr);
0970         for (int j = 0; j < ops[i].values.count(); ++j) {
0971             addModOp(&lmod, mtype, ops[i].attr, &ops[i].values[j]);
0972         }
0973     }
0974 
0975     int retval = ldap_modify_ext_s(ld, dn.toString().toUtf8().data(), lmod, serverctrls, clientctrls);
0976 
0977     ldap_controls_free(serverctrls);
0978     ldap_controls_free(clientctrls);
0979     ldap_mods_free(lmod, 1);
0980     return retval;
0981 }
0982 
0983 int LdapOperation::compare(const LdapDN &dn, const QString &attr, const QByteArray &value)
0984 {
0985     Q_ASSERT(d->mConnection);
0986     LDAP *ld = (LDAP *)d->mConnection->handle();
0987     int msgid;
0988 
0989     LDAPControl **serverctrls = nullptr;
0990     LDAPControl **clientctrls = nullptr;
0991     createControls(&serverctrls, d->mServerCtrls);
0992     createControls(&serverctrls, d->mClientCtrls);
0993 
0994     int vallen = value.size();
0995     BerValue *berval;
0996     berval = (BerValue *)malloc(sizeof(BerValue));
0997     berval->bv_val = (char *)malloc(vallen);
0998     berval->bv_len = vallen;
0999     memcpy(berval->bv_val, value.data(), vallen);
1000 
1001     int retval = ldap_compare_ext(ld, dn.toString().toUtf8().data(), attr.toUtf8().data(), berval, serverctrls, clientctrls, &msgid);
1002 
1003     ber_bvfree(berval);
1004     ldap_controls_free(serverctrls);
1005     ldap_controls_free(clientctrls);
1006 
1007     if (retval == 0) {
1008         retval = msgid;
1009     }
1010     return retval;
1011 }
1012 
1013 int LdapOperation::compare_s(const LdapDN &dn, const QString &attr, const QByteArray &value)
1014 {
1015     Q_ASSERT(d->mConnection);
1016     LDAP *ld = (LDAP *)d->mConnection->handle();
1017 
1018     LDAPControl **serverctrls = nullptr;
1019     LDAPControl **clientctrls = nullptr;
1020     createControls(&serverctrls, d->mServerCtrls);
1021     createControls(&serverctrls, d->mClientCtrls);
1022 
1023     int vallen = value.size();
1024     BerValue *berval;
1025     berval = (BerValue *)malloc(sizeof(BerValue));
1026     berval->bv_val = (char *)malloc(vallen);
1027     berval->bv_len = vallen;
1028     memcpy(berval->bv_val, value.data(), vallen);
1029 
1030     int retval = ldap_compare_ext_s(ld, dn.toString().toUtf8().data(), attr.toUtf8().data(), berval, serverctrls, clientctrls);
1031 
1032     ber_bvfree(berval);
1033     ldap_controls_free(serverctrls);
1034     ldap_controls_free(clientctrls);
1035 
1036     return retval;
1037 }
1038 
1039 int LdapOperation::exop(const QString &oid, const QByteArray &data)
1040 {
1041     Q_ASSERT(d->mConnection);
1042 #if HAVE_LDAP_EXTENDED_OPERATION
1043     LDAP *ld = (LDAP *)d->mConnection->handle();
1044     int msgid;
1045 
1046     LDAPControl **serverctrls = nullptr;
1047     LDAPControl **clientctrls = nullptr;
1048     createControls(&serverctrls, d->mServerCtrls);
1049     createControls(&serverctrls, d->mClientCtrls);
1050 
1051     int vallen = data.size();
1052     BerValue *berval;
1053     berval = (BerValue *)malloc(sizeof(BerValue));
1054     berval->bv_val = (char *)malloc(vallen);
1055     berval->bv_len = vallen;
1056     memcpy(berval->bv_val, data.data(), vallen);
1057 
1058     int retval = ldap_extended_operation(ld, oid.toUtf8().data(), berval, serverctrls, clientctrls, &msgid);
1059 
1060     ber_bvfree(berval);
1061     ldap_controls_free(serverctrls);
1062     ldap_controls_free(clientctrls);
1063 
1064     if (retval == 0) {
1065         retval = msgid;
1066     }
1067     return retval;
1068 #else
1069     qCritical() << "Your LDAP client libraries don't support extended operations.";
1070     return -1;
1071 #endif
1072 }
1073 
1074 int LdapOperation::exop_s(const QString &oid, const QByteArray &data)
1075 {
1076 #if HAVE_LDAP_EXTENDED_OPERATION_S
1077     Q_ASSERT(d->mConnection);
1078     LDAP *ld = (LDAP *)d->mConnection->handle();
1079     BerValue *retdata;
1080     char *retoid;
1081 
1082     LDAPControl **serverctrls = nullptr;
1083     LDAPControl **clientctrls = nullptr;
1084     createControls(&serverctrls, d->mServerCtrls);
1085     createControls(&serverctrls, d->mClientCtrls);
1086 
1087     int vallen = data.size();
1088     BerValue *berval;
1089     berval = (BerValue *)malloc(sizeof(BerValue));
1090     berval->bv_val = (char *)malloc(vallen);
1091     berval->bv_len = vallen;
1092     memcpy(berval->bv_val, data.data(), vallen);
1093 
1094     int retval = ldap_extended_operation_s(ld, oid.toUtf8().data(), berval, serverctrls, clientctrls, &retoid, &retdata);
1095 
1096     ber_bvfree(berval);
1097     ber_bvfree(retdata);
1098     free(retoid);
1099     ldap_controls_free(serverctrls);
1100     ldap_controls_free(clientctrls);
1101 
1102     return retval;
1103 #else
1104     qCritical() << "Your LDAP client libraries don't support extended operations.";
1105     return -1;
1106 #endif
1107 }
1108 
1109 int LdapOperation::abandon(int id)
1110 {
1111     Q_ASSERT(d->mConnection);
1112     LDAP *ld = (LDAP *)d->mConnection->handle();
1113 
1114     LDAPControl **serverctrls = nullptr;
1115     LDAPControl **clientctrls = nullptr;
1116     createControls(&serverctrls, d->mServerCtrls);
1117     createControls(&serverctrls, d->mClientCtrls);
1118 
1119     int retval = ldap_abandon_ext(ld, id, serverctrls, clientctrls);
1120 
1121     ldap_controls_free(serverctrls);
1122     ldap_controls_free(clientctrls);
1123 
1124     return retval;
1125 }
1126 
1127 int LdapOperation::waitForResult(int id, int msecs)
1128 {
1129     Q_ASSERT(d->mConnection);
1130     LDAP *ld = (LDAP *)d->mConnection->handle();
1131 
1132     LDAPMessage *msg;
1133 
1134     QElapsedTimer stopWatch;
1135     stopWatch.start();
1136     int attempt(1);
1137     int timeout(0);
1138 
1139     do {
1140         // Calculate the timeout value to use and assign it to a timeval structure
1141         // see man select (2) for details
1142         timeout = kldap_timeout_value(msecs, stopWatch.elapsed());
1143         qCDebug(LDAP_LOG) << "(" << id << "," << msecs << "): Waiting" << timeout << "msecs for result. Attempt #" << attempt++;
1144         struct timeval tv;
1145         tv.tv_sec = timeout / 1000;
1146         tv.tv_usec = (timeout % 1000) * 1000;
1147 
1148         // Wait for a result
1149         int rescode = ldap_result(ld, id, 0, timeout < 0 ? nullptr : &tv, &msg);
1150         if (rescode == -1) {
1151             return -1;
1152         }
1153         // Act on the return code
1154         if (rescode != 0) {
1155             // Some kind of result is available for processing
1156             return d->processResult(rescode, msg);
1157         }
1158     } while (msecs == -1 || stopWatch.elapsed() < msecs);
1159 
1160     return 0; // timeout
1161 }
1162 
1163 #else
1164 
1165 int LdapOperation::bind(const QByteArray &creds, SASL_Callback_Proc *saslproc, void *data)
1166 {
1167     qCritical() << "LDAP support not compiled";
1168     return -1;
1169 }
1170 
1171 int LdapOperation::bind_s(SASL_Callback_Proc *saslproc, void *data)
1172 {
1173     qCritical() << "LDAP support not compiled";
1174     return -1;
1175 }
1176 
1177 int LdapOperation::search(const LdapDN &base, LdapUrl::Scope scope, const QString &filter, const QStringList &attributes)
1178 {
1179     qCritical() << "LDAP support not compiled";
1180     return -1;
1181 }
1182 
1183 int LdapOperation::add(const LdapObject &object)
1184 {
1185     qCritical() << "LDAP support not compiled";
1186     return -1;
1187 }
1188 
1189 int LdapOperation::add_s(const LdapObject &object)
1190 {
1191     qCritical() << "LDAP support not compiled";
1192     return -1;
1193 }
1194 
1195 int LdapOperation::add(const LdapDN &dn, const ModOps &ops)
1196 {
1197     qCritical() << "LDAP support not compiled";
1198     return -1;
1199 }
1200 
1201 int LdapOperation::add_s(const LdapDN &dn, const ModOps &ops)
1202 {
1203     qCritical() << "LDAP support not compiled";
1204     return -1;
1205 }
1206 
1207 int LdapOperation::rename(const LdapDN &dn, const QString &newRdn, const QString &newSuperior, bool deleteold)
1208 {
1209     qCritical() << "LDAP support not compiled";
1210     return -1;
1211 }
1212 
1213 int LdapOperation::rename_s(const LdapDN &dn, const QString &newRdn, const QString &newSuperior, bool deleteold)
1214 {
1215     qCritical() << "LDAP support not compiled";
1216     return -1;
1217 }
1218 
1219 int LdapOperation::del(const LdapDN &dn)
1220 {
1221     qCritical() << "LDAP support not compiled";
1222     return -1;
1223 }
1224 
1225 int LdapOperation::del_s(const LdapDN &dn)
1226 {
1227     qCritical() << "LDAP support not compiled";
1228     return -1;
1229 }
1230 
1231 int LdapOperation::modify(const LdapDN &dn, const ModOps &ops)
1232 {
1233     qCritical() << "LDAP support not compiled";
1234     return -1;
1235 }
1236 
1237 int LdapOperation::modify_s(const LdapDN &dn, const ModOps &ops)
1238 {
1239     qCritical() << "LDAP support not compiled";
1240     return -1;
1241 }
1242 
1243 int LdapOperation::compare(const LdapDN &dn, const QString &attr, const QByteArray &value)
1244 {
1245     qCritical() << "LDAP support not compiled";
1246     return -1;
1247 }
1248 
1249 int LdapOperation::exop(const QString &oid, const QByteArray &data)
1250 {
1251     qCritical() << "LDAP support not compiled";
1252     return -1;
1253 }
1254 
1255 int LdapOperation::compare_s(const LdapDN &dn, const QString &attr, const QByteArray &value)
1256 {
1257     qCritical() << "LDAP support not compiled";
1258     return -1;
1259 }
1260 
1261 int LdapOperation::exop_s(const QString &oid, const QByteArray &data)
1262 {
1263     qCritical() << "LDAP support not compiled";
1264     return -1;
1265 }
1266 
1267 int LdapOperation::waitForResult(int id, int msecs)
1268 {
1269     qCritical() << "LDAP support not compiled";
1270     return -1;
1271 }
1272 
1273 int LdapOperation::abandon(int id)
1274 {
1275     qCritical() << "LDAP support not compiled";
1276     return -1;
1277 }
1278 
1279 #endif