File indexing completed on 2024-04-28 04:43:51

0001 /*
0002  * Copyright (C) 2008  Barracuda Networks, Inc.
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Lesser General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public
0015  * License along with this library; if not, write to the Free Software
0016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0017  * 02110-1301  USA
0018  *
0019  */
0020 
0021 #include <QLibrary>
0022 #include <QMutex>
0023 #include <QTimer>
0024 #include <QtCrypto>
0025 #include <QtPlugin>
0026 #include <qcaprovider.h>
0027 
0028 #ifndef FORWARD_ONLY
0029 #include <windows.h>
0030 #define SECURITY_WIN32
0031 #include <Security.h>
0032 #endif
0033 
0034 using namespace QCA;
0035 
0036 #define PROVIDER_NAME "qca-wingss"
0037 
0038 #if !defined(FORWARD_ONLY)
0039 
0040 // some defs possibly missing from MinGW
0041 
0042 #ifndef SEC_E_MESSAGE_ALTERED
0043 #define SEC_E_MESSAGE_ALTERED 0x8009030F
0044 #endif
0045 
0046 #ifndef SEC_E_CONTEXT_EXPIRED
0047 #define SEC_E_CONTEXT_EXPIRED 0x80090317
0048 #endif
0049 
0050 #ifndef SEC_E_CRYPTO_SYSTEM_INVALID
0051 #define SEC_E_CRYPTO_SYSTEM_INVALID 0x80090337
0052 #endif
0053 
0054 #ifndef SEC_E_OUT_OF_SEQUENCE
0055 #define SEC_E_OUT_OF_SEQUENCE 0x80090310
0056 #endif
0057 
0058 #ifndef SEC_E_BUFFER_TOO_SMALL
0059 #define SEC_E_BUFFER_TOO_SMALL 0x80090321
0060 #endif
0061 
0062 #ifndef SECURITY_ENTRYPOINTW
0063 #define SECURITY_ENTRYPOINTW TEXT("InitSecurityInterfaceW")
0064 #endif
0065 
0066 #ifndef SECURITY_ENTRYPOINT_ANSIA
0067 #define SECURITY_ENTRYPOINT_ANSIA "InitSecurityInterfaceA"
0068 #endif
0069 
0070 #ifndef SECPKG_FLAG_GSS_COMPATIBLE
0071 #define SECPKG_FLAG_GSS_COMPATIBLE 0x00001000
0072 #endif
0073 
0074 #ifndef SECQOP_WRAP_NO_ENCRYPT
0075 #define SECQOP_WRAP_NO_ENCRYPT 0x80000001
0076 #endif
0077 
0078 #ifndef ISC_RET_MUTUAL_AUTH
0079 #define ISC_RET_MUTUAL_AUTH 0x00000002
0080 #endif
0081 
0082 #ifndef ISC_RET_SEQUENCE_DETECT
0083 #define ISC_RET_SEQUENCE_DETECT 0x00000008
0084 #endif
0085 
0086 #ifndef ISC_RET_CONFIDENTIALITY
0087 #define ISC_RET_CONFIDENTIALITY 0x00000010
0088 #endif
0089 
0090 #ifndef ISC_RET_INTEGRITY
0091 #define ISC_RET_INTEGRITY 0x00010000
0092 #endif
0093 
0094 #ifdef Q_CC_MINGW
0095 
0096 // for some reason, the MinGW definition of the W table has A functions in
0097 //   it, so we define a fixed version to use instead...
0098 
0099 typedef struct _FIXED_SECURITY_FUNCTION_TABLEW
0100 {
0101     unsigned long                     dwVersion;
0102     ENUMERATE_SECURITY_PACKAGES_FN_W  EnumerateSecurityPackagesW;
0103     QUERY_CREDENTIALS_ATTRIBUTES_FN_W QueryCredentialsAttributesW;
0104     ACQUIRE_CREDENTIALS_HANDLE_FN_W   AcquireCredentialsHandleW;
0105     FREE_CREDENTIALS_HANDLE_FN        FreeCredentialsHandle;
0106     void SEC_FAR                     *Reserved2;
0107     INITIALIZE_SECURITY_CONTEXT_FN_W  InitializeSecurityContextW;
0108     ACCEPT_SECURITY_CONTEXT_FN        AcceptSecurityContext;
0109     COMPLETE_AUTH_TOKEN_FN            CompleteAuthToken;
0110     DELETE_SECURITY_CONTEXT_FN        DeleteSecurityContext;
0111     APPLY_CONTROL_TOKEN_FN_W          ApplyControlTokenW;
0112     QUERY_CONTEXT_ATTRIBUTES_FN_W     QueryContextAttributesW;
0113     IMPERSONATE_SECURITY_CONTEXT_FN   ImpersonateSecurityContext;
0114     REVERT_SECURITY_CONTEXT_FN        RevertSecurityContext;
0115     MAKE_SIGNATURE_FN                 MakeSignature;
0116     VERIFY_SIGNATURE_FN               VerifySignature;
0117     FREE_CONTEXT_BUFFER_FN            FreeContextBuffer;
0118     QUERY_SECURITY_PACKAGE_INFO_FN_W  QuerySecurityPackageInfoW;
0119     void SEC_FAR                     *Reserved3;
0120     void SEC_FAR                     *Reserved4;
0121     void SEC_FAR                     *Unknown1;
0122     void SEC_FAR                     *Unknown2;
0123     void SEC_FAR                     *Unknown3;
0124     void SEC_FAR                     *Unknown4;
0125     void SEC_FAR                     *Unknown5;
0126     ENCRYPT_MESSAGE_FN                EncryptMessage;
0127     DECRYPT_MESSAGE_FN                DecryptMessage;
0128 } FixedSecurityFunctionTableW, *PFixedSecurityFunctionTableW;
0129 
0130 typedef FixedSecurityFunctionTableW  MySecurityFunctionTableW;
0131 typedef PFixedSecurityFunctionTableW PMySecurityFunctionTableW;
0132 
0133 #else
0134 
0135 typedef SecurityFunctionTableW  MySecurityFunctionTableW;
0136 typedef PSecurityFunctionTableW PMySecurityFunctionTableW;
0137 
0138 #endif
0139 
0140 #ifdef UNICODE
0141 #define MySecurityFunctionTable MySecurityFunctionTableW
0142 #define PMySecurityFunctionTable PMySecurityFunctionTableW
0143 #else
0144 #define MySecurityFunctionTable MySecurityFunctionTableA
0145 #define PMySecurityFunctionTable PMySecurityFunctionTableA
0146 #endif
0147 
0148 #endif // !defined(FORWARD_ONLY)
0149 
0150 namespace wingssQCAPlugin {
0151 
0152 //----------------------------------------------------------------------------
0153 // SSPI helper API
0154 //----------------------------------------------------------------------------
0155 
0156 typedef void (*sspi_logger_func)(const QString &str);
0157 
0158 class SspiPackage
0159 {
0160 public:
0161     QString name;
0162     quint32 caps;
0163     quint32 maxtok;
0164     quint16 version;
0165     quint16 rpcid;
0166     QString comment;
0167 };
0168 
0169 // logger can be set even when sspi is not loaded (this is needed so logging
0170 //   during sspi_load/unload can be captured).  pass 0 to disable.
0171 void sspi_set_logger(sspi_logger_func p);
0172 
0173 void sspi_log(const QString &str);
0174 
0175 bool sspi_load();
0176 void sspi_unload();
0177 
0178 // returns the available security packages.  only the first call actually
0179 //   queries the sspi subsystem.  subsequent calls return a cached result.
0180 QList<SspiPackage> sspi_get_packagelist();
0181 
0182 // refresh the package list cache.  call sspi_get_packagelist afterwards to
0183 //   get the new list.
0184 void sspi_refresh_packagelist();
0185 
0186 // helper functions for logging
0187 QString SECURITY_STATUS_toString(SECURITY_STATUS i);
0188 QString ptr_toString(const void *p);
0189 
0190 //----------------------------------------------------------------------------
0191 // SSPI helper implementation
0192 //----------------------------------------------------------------------------
0193 
0194 Q_GLOBAL_STATIC(QMutex, sspi_mutex)
0195 Q_GLOBAL_STATIC(QMutex, sspi_logger_mutex)
0196 
0197 union SecurityFunctionTableUnion {
0198     PMySecurityFunctionTableW W;
0199     PSecurityFunctionTableA   A;
0200     void                     *ptr;
0201 };
0202 
0203 static QLibrary                  *sspi_lib = 0;
0204 static SecurityFunctionTableUnion sspi;
0205 static sspi_logger_func           sspi_logger;
0206 static QList<SspiPackage>        *sspi_packagelist = 0;
0207 
0208 void sspi_log(const QString &str)
0209 {
0210     QMutexLocker locker(sspi_logger_mutex());
0211 
0212     if (sspi_logger)
0213         sspi_logger(str);
0214 }
0215 
0216 void sspi_set_logger(sspi_logger_func p)
0217 {
0218     QMutexLocker locker(sspi_logger_mutex());
0219 
0220     sspi_logger = p;
0221 }
0222 
0223 #define CASE_SS_STRING(s)                                                                                              \
0224     case s:                                                                                                            \
0225         return #s;
0226 
0227 static const char *SECURITY_STATUS_lookup(SECURITY_STATUS i)
0228 {
0229     switch (i) {
0230         CASE_SS_STRING(SEC_E_OK);
0231         CASE_SS_STRING(SEC_I_COMPLETE_AND_CONTINUE);
0232         CASE_SS_STRING(SEC_I_COMPLETE_NEEDED);
0233         CASE_SS_STRING(SEC_I_CONTINUE_NEEDED);
0234         CASE_SS_STRING(SEC_I_INCOMPLETE_CREDENTIALS);
0235         CASE_SS_STRING(SEC_E_UNSUPPORTED_FUNCTION);
0236         CASE_SS_STRING(SEC_E_INVALID_TOKEN);
0237         CASE_SS_STRING(SEC_E_MESSAGE_ALTERED);
0238         CASE_SS_STRING(SEC_E_INSUFFICIENT_MEMORY);
0239         CASE_SS_STRING(SEC_E_INTERNAL_ERROR);
0240         CASE_SS_STRING(SEC_E_INVALID_HANDLE);
0241         CASE_SS_STRING(SEC_E_LOGON_DENIED);
0242         CASE_SS_STRING(SEC_E_NO_AUTHENTICATING_AUTHORITY);
0243         CASE_SS_STRING(SEC_E_NO_CREDENTIALS);
0244         CASE_SS_STRING(SEC_E_TARGET_UNKNOWN);
0245         CASE_SS_STRING(SEC_E_WRONG_PRINCIPAL);
0246         CASE_SS_STRING(SEC_E_BUFFER_TOO_SMALL);
0247         CASE_SS_STRING(SEC_E_CONTEXT_EXPIRED);
0248         CASE_SS_STRING(SEC_E_CRYPTO_SYSTEM_INVALID);
0249         CASE_SS_STRING(SEC_E_QOP_NOT_SUPPORTED);
0250         CASE_SS_STRING(SEC_E_INCOMPLETE_MESSAGE);
0251         CASE_SS_STRING(SEC_E_OUT_OF_SEQUENCE);
0252     default:
0253         break;
0254     }
0255     return 0;
0256 }
0257 
0258 QString SECURITY_STATUS_toString(SECURITY_STATUS i)
0259 {
0260     const char *str = SECURITY_STATUS_lookup(i);
0261     if (str)
0262         return QString(str);
0263     else
0264         return QString::number(i);
0265 }
0266 
0267 QString ptr_toString(const void *p)
0268 {
0269     return QString().sprintf("%p", p);
0270 }
0271 
0272 bool sspi_load()
0273 {
0274     QMutexLocker locker(sspi_mutex());
0275     if (sspi_lib)
0276         return true;
0277 
0278     sspi_lib = new QLibrary("secur32");
0279     if (!sspi_lib->load()) {
0280         delete sspi_lib;
0281         sspi_lib = 0;
0282         return false;
0283     }
0284 
0285     union {
0286         INIT_SECURITY_INTERFACE_W W;
0287         INIT_SECURITY_INTERFACE_A A;
0288         void                     *ptr;
0289     } pInitSecurityInterface;
0290     pInitSecurityInterface.ptr = 0;
0291 
0292     QString securityEntrypoint;
0293     securityEntrypoint       = QString::fromUtf16((const ushort *)SECURITY_ENTRYPOINTW);
0294     pInitSecurityInterface.W = (INIT_SECURITY_INTERFACE_W)(sspi_lib->resolve(securityEntrypoint.toLatin1().data()));
0295     if (!pInitSecurityInterface.ptr) {
0296         sspi_lib->unload();
0297         delete sspi_lib;
0298         sspi_lib = 0;
0299         return false;
0300     }
0301 
0302     union {
0303         PMySecurityFunctionTableW W;
0304         PSecurityFunctionTableA   A;
0305         void                     *ptr;
0306     } funcs;
0307     funcs.ptr = 0;
0308 
0309     funcs.W = (PMySecurityFunctionTableW)pInitSecurityInterface.W();
0310 
0311     sspi_log(QString("%1() = %2\n").arg(securityEntrypoint, ptr_toString(funcs.ptr)));
0312     if (!funcs.ptr) {
0313         sspi_lib->unload();
0314         delete sspi_lib;
0315         sspi_lib = 0;
0316         return false;
0317     }
0318 
0319     sspi.W = funcs.W;
0320 
0321     return true;
0322 }
0323 
0324 void sspi_unload()
0325 {
0326     QMutexLocker locker(sspi_mutex());
0327 
0328     sspi_lib->unload();
0329     delete sspi_lib;
0330     sspi_lib = 0;
0331     sspi.ptr = 0;
0332 }
0333 
0334 static QList<SspiPackage> sspi_get_packagelist_direct()
0335 {
0336     QList<SspiPackage> out;
0337 
0338     ULONG           cPackages;
0339     SecPkgInfoW    *pPackageInfo;
0340     SECURITY_STATUS ret = sspi.W->EnumerateSecurityPackagesW(&cPackages, &pPackageInfo);
0341     sspi_log(QString("EnumerateSecurityPackages() = %1\n").arg(SECURITY_STATUS_toString(ret)));
0342     if (ret != SEC_E_OK)
0343         return out;
0344 
0345     for (int n = 0; n < (int)cPackages; ++n) {
0346         SecPkgInfoW *p = &pPackageInfo[n];
0347         SspiPackage  i;
0348         i.name    = QString::fromUtf16((const ushort *)p->Name);
0349         i.caps    = p->fCapabilities;
0350         i.version = p->wVersion;
0351         i.rpcid   = p->wRPCID;
0352         i.maxtok  = p->cbMaxToken;
0353         i.comment = QString::fromUtf16((const ushort *)p->Comment);
0354         out += i;
0355     }
0356 
0357     ret = sspi.W->FreeContextBuffer(&pPackageInfo);
0358     sspi_log(QString("FreeContextBuffer() = %1\n").arg(SECURITY_STATUS_toString(ret)));
0359 
0360     return out;
0361 }
0362 
0363 static void sspi_refresh_packagelist_internal()
0364 {
0365     if (sspi_packagelist)
0366         *sspi_packagelist = sspi_get_packagelist_direct();
0367     else
0368         sspi_packagelist = new QList<SspiPackage>(sspi_get_packagelist_direct());
0369 }
0370 
0371 QList<SspiPackage> sspi_get_packagelist()
0372 {
0373     QMutexLocker locker(sspi_mutex());
0374 
0375     if (!sspi_packagelist)
0376         sspi_refresh_packagelist_internal();
0377     return *sspi_packagelist;
0378 }
0379 
0380 void sspi_refresh_packagelist()
0381 {
0382     QMutexLocker locker(sspi_mutex());
0383 
0384     sspi_refresh_packagelist_internal();
0385 }
0386 
0387 template<typename T> inline T cap_to_int(const T &t)
0388 {
0389     if (sizeof(int) <= sizeof(T))
0390         return (int)((t > INT_MAX) ? INT_MAX : t);
0391     else
0392         return (int)t;
0393 }
0394 
0395 //----------------------------------------------------------------------------
0396 // KerberosSession
0397 //----------------------------------------------------------------------------
0398 // this class thinly wraps SSPI to perform kerberos.
0399 class KerberosSession
0400 {
0401 public:
0402     enum ReturnCode
0403     {
0404         Success,
0405         NeedMoreData, // for decrypt
0406         ErrorInvalidSystem,
0407         ErrorKerberosNotFound,
0408         ErrorAcquireCredentials,
0409         ErrorInitialize,
0410         ErrorQueryContext,
0411         ErrorEncrypt,
0412         ErrorDecrypt
0413     };
0414 
0415     SECURITY_STATUS lastErrorCode;
0416 
0417     quint32 maxtok;
0418 
0419     bool       initialized;
0420     bool       first_step;
0421     QByteArray first_out_token;
0422     bool       authed;
0423 
0424     QString spn;
0425 
0426     CredHandle user_cred;
0427     TimeStamp  user_cred_expiry;
0428 
0429     CtxtHandle                ctx;
0430     ULONG                     ctx_attr_req;
0431     ULONG                     ctx_attr;
0432     bool                      have_sizes;
0433     SecPkgContext_Sizes       ctx_sizes;
0434     SecPkgContext_StreamSizes ctx_streamSizes;
0435 
0436     KerberosSession()
0437         : initialized(false)
0438         , have_sizes(false)
0439     {
0440     }
0441 
0442     ~KerberosSession()
0443     {
0444         if (initialized) {
0445             SECURITY_STATUS ret = sspi.W->DeleteSecurityContext(&ctx);
0446             sspi_log(QString("DeleteSecurityContext() = %1\n").arg(SECURITY_STATUS_toString(ret)));
0447 
0448             ret = sspi.W->FreeCredentialsHandle(&user_cred);
0449             sspi_log(QString("FreeCredentialsHandle() = %1\n").arg(SECURITY_STATUS_toString(ret)));
0450         }
0451     }
0452 
0453     ReturnCode init(const QString &_spn)
0454     {
0455         // ensure kerberos is available
0456         bool               found    = false;
0457         quint32            _maxtok  = 0;
0458         QList<SspiPackage> packages = sspi_get_packagelist();
0459         sspi_log("SSPI packages:\n");
0460         foreach (const SspiPackage &p, packages) {
0461             bool gss = false;
0462             if (p.caps & SECPKG_FLAG_GSS_COMPATIBLE)
0463                 gss = true;
0464 
0465             if (p.name == "Kerberos" && gss) {
0466                 found   = true;
0467                 _maxtok = p.maxtok;
0468             }
0469 
0470             QString gssstr = gss ? "yes" : "no";
0471             sspi_log(QString("  %1 (gss=%2, maxtok=%3)\n").arg(p.name, gssstr, QString::number(p.maxtok)));
0472         }
0473 
0474         if (!found)
0475             return ErrorKerberosNotFound;
0476 
0477         // get the logged-in user's credentials
0478         SECURITY_STATUS ret = sspi.W->AcquireCredentialsHandleW((SEC_WCHAR *)0, // we want creds of logged-in user
0479                                                                 (SEC_WCHAR *)QString("Kerberos").utf16(),
0480                                                                 SECPKG_CRED_OUTBOUND,
0481                                                                 0, // don't need a LUID
0482                                                                 0, // default credentials for kerberos
0483                                                                 0, // not used
0484                                                                 0, // not used
0485                                                                 &user_cred,
0486                                                                 &user_cred_expiry);
0487         sspi_log(QString("AcquireCredentialsHandle() = %1\n").arg(SECURITY_STATUS_toString(ret)));
0488         if (ret != SEC_E_OK) {
0489             lastErrorCode = ret;
0490             return ErrorAcquireCredentials;
0491         }
0492 
0493         maxtok = _maxtok;
0494         authed = false;
0495         spn    = _spn;
0496 
0497         SecBuffer outbuf;
0498         outbuf.BufferType = SECBUFFER_TOKEN;
0499         outbuf.cbBuffer   = 0;
0500         outbuf.pvBuffer   = NULL;
0501 
0502         SecBufferDesc outbufdesc;
0503         outbufdesc.ulVersion = SECBUFFER_VERSION;
0504         outbufdesc.cBuffers  = 1;
0505         outbufdesc.pBuffers  = &outbuf;
0506 
0507         ctx_attr_req = 0;
0508 
0509         // not strictly required, but some SSPI calls seem to always
0510         //   allocate memory, so for consistency we'll explicity
0511         //   request to have it that way all the time
0512         ctx_attr_req |= ISC_REQ_ALLOCATE_MEMORY;
0513 
0514         // required by SASL GSSAPI RFC
0515         ctx_attr_req |= ISC_REQ_INTEGRITY;
0516 
0517         // required for security layer
0518         ctx_attr_req |= ISC_REQ_MUTUAL_AUTH;
0519         ctx_attr_req |= ISC_REQ_SEQUENCE_DETECT;
0520 
0521         // required for encryption
0522         ctx_attr_req |= ISC_REQ_CONFIDENTIALITY;
0523 
0524         // other options that may be of use, but we currently aren't
0525         //   using:
0526         // ISC_REQ_DELEGATE
0527         // ISC_REQ_REPLAY_DETECT
0528 
0529         ret = sspi.W->InitializeSecurityContextW(&user_cred,
0530                                                  0, // NULL for the first call
0531                                                  (SEC_WCHAR *)spn.utf16(),
0532                                                  ctx_attr_req,
0533                                                  0,
0534                                                  SECURITY_NETWORK_DREP,
0535                                                  0, // NULL for first call
0536                                                  0,
0537                                                  &ctx,
0538                                                  &outbufdesc,
0539                                                  &ctx_attr,
0540                                                  0); // don't care about expiration
0541         sspi_log(QString("InitializeSecurityContext(*, 0, ...) = %1\n").arg(SECURITY_STATUS_toString(ret)));
0542         if (ret == SEC_E_OK || ret == SEC_I_CONTINUE_NEEDED) {
0543             if (outbuf.pvBuffer) {
0544                 first_out_token.resize(outbuf.cbBuffer);
0545                 memcpy(first_out_token.data(), outbuf.pvBuffer, outbuf.cbBuffer);
0546 
0547                 SECURITY_STATUS fret = sspi.W->FreeContextBuffer(outbuf.pvBuffer);
0548                 sspi_log(QString("FreeContextBuffer() = %1\n").arg(SECURITY_STATUS_toString(fret)));
0549             }
0550 
0551             if (ret == SEC_E_OK)
0552                 authed = true;
0553         } else {
0554             // ret was an error, or some unexpected value like
0555             //   SEC_I_COMPLETE_NEEDED or
0556             //   SEC_I_COMPLETE_AND_CONTINUE, which i believe are
0557             //   not used for kerberos
0558 
0559             lastErrorCode = ret;
0560 
0561             ret = sspi.W->FreeCredentialsHandle(&user_cred);
0562             sspi_log(QString("FreeCredentialsHandle() = %1\n").arg(SECURITY_STATUS_toString(ret)));
0563 
0564             return ErrorInitialize;
0565         }
0566 
0567         initialized = true;
0568         first_step  = true;
0569 
0570         return Success;
0571     }
0572 
0573     ReturnCode step(const QByteArray &in, QByteArray *out, bool *authenticated)
0574     {
0575         if (authed) {
0576             out->clear();
0577             *authenticated = true;
0578             return Success;
0579         }
0580 
0581         if (first_step) {
0582             // ignore 'in'
0583 
0584             *out = first_out_token;
0585             first_out_token.clear();
0586 
0587             first_step = false;
0588         } else {
0589             SecBuffer outbuf;
0590             outbuf.BufferType = SECBUFFER_TOKEN;
0591             outbuf.cbBuffer   = 0;
0592             outbuf.pvBuffer   = NULL;
0593 
0594             SecBufferDesc outbufdesc;
0595             outbufdesc.ulVersion = SECBUFFER_VERSION;
0596             outbufdesc.cBuffers  = 1;
0597             outbufdesc.pBuffers  = &outbuf;
0598 
0599             SecBuffer inbuf;
0600             inbuf.BufferType = SECBUFFER_TOKEN;
0601             inbuf.cbBuffer   = in.size();
0602             inbuf.pvBuffer   = (void *)in.data();
0603 
0604             SecBufferDesc inbufdesc;
0605             inbufdesc.ulVersion = SECBUFFER_VERSION;
0606             inbufdesc.cBuffers  = 1;
0607             inbufdesc.pBuffers  = &inbuf;
0608 
0609             SECURITY_STATUS ret = sspi.W->InitializeSecurityContextW(&user_cred,
0610                                                                      &ctx,
0611                                                                      (SEC_WCHAR *)spn.utf16(),
0612                                                                      ctx_attr_req,
0613                                                                      0,
0614                                                                      SECURITY_NETWORK_DREP,
0615                                                                      &inbufdesc,
0616                                                                      0,
0617                                                                      &ctx,
0618                                                                      &outbufdesc,
0619                                                                      &ctx_attr,
0620                                                                      0); // don't care about expiration
0621             sspi_log(QString("InitializeSecurityContext(*, ctx, ...) = %1\n").arg(SECURITY_STATUS_toString(ret)));
0622             if (ret == SEC_E_OK || ret == SEC_I_CONTINUE_NEEDED) {
0623                 if (outbuf.pvBuffer) {
0624                     out->resize(outbuf.cbBuffer);
0625                     memcpy(out->data(), outbuf.pvBuffer, outbuf.cbBuffer);
0626 
0627                     SECURITY_STATUS fret = sspi.W->FreeContextBuffer(outbuf.pvBuffer);
0628                     sspi_log(QString("FreeContextBuffer() = %1\n").arg(SECURITY_STATUS_toString(fret)));
0629                 } else
0630                     out->clear();
0631 
0632                 if (ret == SEC_E_OK)
0633                     authed = true;
0634             } else {
0635                 // ret was an error, or some unexpected value like
0636                 //   SEC_I_COMPLETE_NEEDED or
0637                 //   SEC_I_COMPLETE_AND_CONTINUE, which i believe are
0638                 //   not used for kerberos
0639 
0640                 lastErrorCode = ret;
0641 
0642                 ret = sspi.W->DeleteSecurityContext(&ctx);
0643                 sspi_log(QString("DeleteSecurityContext() = %1\n").arg(SECURITY_STATUS_toString(ret)));
0644 
0645                 ret = sspi.W->FreeCredentialsHandle(&user_cred);
0646                 sspi_log(QString("FreeCredentialsHandle() = %1\n").arg(SECURITY_STATUS_toString(ret)));
0647 
0648                 initialized = false;
0649                 return ErrorInitialize;
0650             }
0651         }
0652 
0653         *authenticated = authed;
0654         return Success;
0655     }
0656 
0657     // private
0658     bool ensure_sizes_cached()
0659     {
0660         if (!have_sizes) {
0661             SECURITY_STATUS ret = sspi.W->QueryContextAttributesW(&ctx, SECPKG_ATTR_SIZES, &ctx_sizes);
0662             sspi_log(QString("QueryContextAttributes(ctx, SECPKG_ATTR_SIZES, ...) = %1\n")
0663                          .arg(SECURITY_STATUS_toString(ret)));
0664             if (ret != SEC_E_OK) {
0665                 lastErrorCode = ret;
0666                 return false;
0667             }
0668 
0669             // for some reason, querying the stream sizes returns
0670             //   SEC_E_UNSUPPORTED_FUNCTION on my system, even
0671             //   though the docs say it should work and putty
0672             //   wingss also calls it.
0673 
0674             // all we really need is cbMaximumMessage, and since
0675             //   we can't query for it, we'll hard code some
0676             //   value.  according to putty wingss, the max size
0677             //   is essentially unbounded anyway, so this should
0678             //   be safe to do.
0679             ctx_streamSizes.cbMaximumMessage = 8192;
0680 
0681             // ret = sspi.W->QueryContextAttributesW(&ctx, SECPKG_ATTR_STREAM_SIZES, &ctx_streamSizes);
0682             // sspi_log(QString("QueryContextAttributes(ctx, SECPKG_ATTR_STREAM_SIZES, ...) =
0683             // %1\n").arg(SECURITY_STATUS_toString(ret))); if(ret != SEC_E_OK)
0684             //{
0685             //  lastErrorCode = ret;
0686             //  return ErrorQueryContext;
0687             //}
0688 
0689             have_sizes = true;
0690         }
0691 
0692         return true;
0693     }
0694 
0695     ReturnCode get_max_encrypt_size(int *max)
0696     {
0697         if (!ensure_sizes_cached())
0698             return ErrorQueryContext;
0699 
0700         *max = cap_to_int<unsigned long>(ctx_streamSizes.cbMaximumMessage);
0701 
0702         return Success;
0703     }
0704 
0705     ReturnCode encode(const QByteArray &in, QByteArray *out, bool encrypt)
0706     {
0707         if (!ensure_sizes_cached())
0708             return ErrorQueryContext;
0709 
0710         QByteArray tokenbuf(ctx_sizes.cbSecurityTrailer, 0);
0711         QByteArray padbuf(ctx_sizes.cbBlockSize, 0);
0712 
0713         // we assume here, like putty wingss, that the output size is
0714         //   less than or equal to the input size.  honestly I don't
0715         //   see how this is clear from the SSPI documentation, but
0716         //   the code seems to work so we'll go with it...
0717         QByteArray databuf = in;
0718 
0719         SecBuffer buf[3];
0720         buf[0].BufferType = SECBUFFER_TOKEN;
0721         buf[0].cbBuffer   = tokenbuf.size();
0722         buf[0].pvBuffer   = tokenbuf.data();
0723         buf[1].BufferType = SECBUFFER_DATA;
0724         buf[1].cbBuffer   = databuf.size();
0725         buf[1].pvBuffer   = databuf.data();
0726         buf[2].BufferType = SECBUFFER_PADDING;
0727         buf[2].cbBuffer   = padbuf.size();
0728         buf[2].pvBuffer   = padbuf.data();
0729 
0730         SecBufferDesc bufdesc;
0731         bufdesc.ulVersion = SECBUFFER_VERSION;
0732         bufdesc.cBuffers  = 3;
0733         bufdesc.pBuffers  = buf;
0734 
0735         SECURITY_STATUS ret = sspi.W->EncryptMessage(&ctx, encrypt ? 0 : SECQOP_WRAP_NO_ENCRYPT, &bufdesc, 0);
0736         sspi_log(QString("EncryptMessage() = %1\n").arg(SECURITY_STATUS_toString(ret)));
0737         if (ret != SEC_E_OK) {
0738             lastErrorCode = ret;
0739             return ErrorEncrypt;
0740         }
0741 
0742         QByteArray fullbuf;
0743         for (int i = 0; i < (int)bufdesc.cBuffers; ++i)
0744             fullbuf += QByteArray((const char *)bufdesc.pBuffers[i].pvBuffer, bufdesc.pBuffers[i].cbBuffer);
0745 
0746         *out = fullbuf;
0747         return Success;
0748     }
0749 
0750     ReturnCode decode(const QByteArray &in, QByteArray *out, bool *encrypted)
0751     {
0752         SecBuffer buf[2];
0753         buf[0].BufferType = SECBUFFER_DATA;
0754         buf[0].cbBuffer   = 0;
0755         buf[0].pvBuffer   = NULL;
0756         buf[1].BufferType = SECBUFFER_STREAM;
0757         buf[1].cbBuffer   = in.size();
0758         buf[1].pvBuffer   = (void *)in.data();
0759 
0760         SecBufferDesc bufdesc;
0761         bufdesc.ulVersion = SECBUFFER_VERSION;
0762         bufdesc.cBuffers  = 2;
0763         bufdesc.pBuffers  = buf;
0764 
0765         ULONG           fQOP;
0766         SECURITY_STATUS ret = sspi.W->DecryptMessage(&ctx, &bufdesc, 0, &fQOP);
0767         sspi_log(QString("DecryptMessage() = %1\n").arg(SECURITY_STATUS_toString(ret)));
0768         if (ret == SEC_E_INCOMPLETE_MESSAGE) {
0769             return NeedMoreData;
0770         } else if (ret != SEC_E_OK) {
0771             lastErrorCode = ret;
0772             return ErrorDecrypt;
0773         }
0774 
0775         if (buf[0].pvBuffer) {
0776             out->resize(buf[0].cbBuffer);
0777             memcpy(out->data(), buf[0].pvBuffer, buf[0].cbBuffer);
0778 
0779             SECURITY_STATUS ret = sspi.W->FreeContextBuffer(buf[0].pvBuffer);
0780             sspi_log(QString("FreeContextBuffer() = %1\n").arg(SECURITY_STATUS_toString(ret)));
0781         } else
0782             out->clear();
0783 
0784         if (fQOP & SECQOP_WRAP_NO_ENCRYPT)
0785             *encrypted = false;
0786         else
0787             *encrypted = true;
0788 
0789         return Success;
0790     }
0791 };
0792 
0793 //----------------------------------------------------------------------------
0794 // SaslGssapiSession
0795 //----------------------------------------------------------------------------
0796 // this class wraps KerberosSession to perform SASL GSSAPI.  it hides away
0797 //   any SSPI details, and is thus very simple to use.
0798 class SaslGssapiSession
0799 {
0800 private:
0801     int             secflags;
0802     KerberosSession sess;
0803     int             mode; // 0 = kerberos tokens, 1 = app packets
0804     bool            authed;
0805     QByteArray      inbuf;
0806 
0807     int max_enc_size; // most we can encrypt to them
0808     int max_dec_size; // most we are expected to decrypt from them
0809 
0810 public:
0811     enum SecurityFlags
0812     {
0813         // only one of these should be set
0814         RequireAtLeastInt = 0x0001,
0815         RequireConf       = 0x0002
0816     };
0817 
0818     enum ReturnCode
0819     {
0820         Success,
0821         ErrorInit,
0822         ErrorKerberosStep,
0823         ErrorAppTokenDecode,
0824         ErrorAppTokenIsEncrypted,
0825         ErrorAppTokenWrongSize,
0826         ErrorAppTokenInvalid,
0827         ErrorAppTokenEncode,
0828         ErrorLayerTooWeak,
0829         ErrorEncode,
0830         ErrorDecode,
0831         ErrorDecodeTooLarge,
0832         ErrorDecodeNotEncrypted
0833     };
0834 
0835     // set this before auth, if you want
0836     QString authzid;
0837 
0838     // read-only
0839     bool do_layer, do_conf;
0840 
0841     SaslGssapiSession()
0842     {
0843     }
0844 
0845     ReturnCode init(const QString &proto, const QString &fqdn, int _secflags)
0846     {
0847         secflags = _secflags;
0848         mode     = 0; // kerberos tokens
0849         authed   = false;
0850 
0851         do_layer = false;
0852         do_conf  = false;
0853 
0854         if (sess.init(proto + '/' + fqdn) != KerberosSession::Success)
0855             return ErrorInit;
0856 
0857         return Success;
0858     }
0859 
0860     ReturnCode step(const QByteArray &in, QByteArray *out, bool *authenticated)
0861     {
0862         if (authed) {
0863             out->clear();
0864             *authenticated = true;
0865             return Success;
0866         }
0867 
0868         if (mode == 0) // kerberos tokens
0869         {
0870             bool kerb_authed;
0871             if (sess.step(in, out, &kerb_authed) != KerberosSession::Success)
0872                 return ErrorKerberosStep;
0873 
0874             if (kerb_authed)
0875                 mode = 1; // switch to app packets
0876 
0877             *authenticated = false;
0878         } else if (mode == 1) {
0879             bool layerPossible      = false;
0880             bool encryptionPossible = false;
0881             if (sess.ctx_attr & ISC_RET_INTEGRITY && sess.ctx_attr & ISC_RET_MUTUAL_AUTH &&
0882                 sess.ctx_attr & ISC_RET_SEQUENCE_DETECT) {
0883                 layerPossible = true;
0884 
0885                 if (sess.ctx_attr & ISC_RET_CONFIDENTIALITY)
0886                     encryptionPossible = true;
0887             }
0888 
0889             if (layerPossible) {
0890                 if (encryptionPossible)
0891                     sspi_log("Kerberos application data protection supported (with encryption)\n");
0892                 else
0893                     sspi_log("Kerberos application data protection supported (without encryption)\n");
0894             } else
0895                 sspi_log("No Kerberos application data protection supported\n");
0896 
0897             QByteArray decbuf;
0898             bool       encrypted;
0899             if (sess.decode(in, &decbuf, &encrypted) != KerberosSession::Success) {
0900                 sspi_log("Error decoding application token\n");
0901                 return ErrorAppTokenDecode;
0902             }
0903 
0904             // this packet is supposed to be not encrypted
0905             if (encrypted) {
0906                 sspi_log("Error, application token is encrypted\n");
0907                 return ErrorAppTokenIsEncrypted;
0908             }
0909 
0910             // packet must be exactly 4 bytes
0911             if (decbuf.size() != 4) {
0912                 sspi_log("Error, application token is the wrong size\n");
0913                 return ErrorAppTokenWrongSize;
0914             }
0915 
0916             QString str;
0917             str.sprintf("%02x%02x%02x%02x",
0918                         (unsigned int)decbuf[0],
0919                         (unsigned int)decbuf[1],
0920                         (unsigned int)decbuf[2],
0921                         (unsigned int)decbuf[3]);
0922             sspi_log(QString("Received application token: [%1]\n").arg(str));
0923 
0924             unsigned char layermask = decbuf[0];
0925             quint32       maxsize   = 0;
0926             maxsize += (unsigned char)decbuf[1];
0927             maxsize <<= 8;
0928             maxsize += (unsigned char)decbuf[2];
0929             maxsize <<= 8;
0930             maxsize += (unsigned char)decbuf[3];
0931 
0932             // if 'None' is all that is supported, then maxsize
0933             //   must be zero
0934             if (layermask == 1 && maxsize > 0) {
0935                 sspi_log("Error, supports no security layer but the max buffer size is not zero\n");
0936                 return ErrorAppTokenInvalid;
0937             }
0938 
0939             // convert maxsize to a signed int, by capping it
0940             int _max_enc_size = cap_to_int<quint32>(maxsize);
0941 
0942             // parse out layermask
0943             bool        saslLayerNone = false;
0944             bool        saslLayerInt  = false;
0945             bool        saslLayerConf = false;
0946             QStringList saslLayerModes;
0947             if (layermask & 1) {
0948                 saslLayerNone = true;
0949                 saslLayerModes += "None";
0950             }
0951             if (layermask & 2) {
0952                 saslLayerInt = true;
0953                 saslLayerModes += "Int";
0954             }
0955             if (layermask & 4) {
0956                 saslLayerConf = true;
0957                 saslLayerModes += "Conf";
0958             }
0959 
0960             sspi_log(QString("Security layer modes supported: %1\n").arg(saslLayerModes.join(", ")));
0961             sspi_log(QString("Security layer max packet size: %1\n").arg(maxsize));
0962 
0963             // create outbound application token
0964             QByteArray obuf(4, 0); // initially 4 bytes
0965 
0966             // set one of use_conf or use_int, but not both
0967             bool use_conf = false;
0968             bool use_int  = false;
0969             if (encryptionPossible && saslLayerConf)
0970                 use_conf = true;
0971             else if (layerPossible && saslLayerInt)
0972                 use_int = true;
0973             else if (!saslLayerNone) {
0974                 sspi_log("Error, no compatible layer mode supported, not even 'None'\n");
0975                 return ErrorLayerTooWeak;
0976             }
0977 
0978             if ((secflags & RequireConf) && !use_conf) {
0979                 sspi_log("Error, 'Conf' required but not supported\n");
0980                 return ErrorLayerTooWeak;
0981             }
0982 
0983             if ((secflags & RequireAtLeastInt) && !use_conf && !use_int) {
0984                 sspi_log("Error, 'Conf' or 'Int' required but not supported\n");
0985                 return ErrorLayerTooWeak;
0986             }
0987 
0988             if (use_conf) {
0989                 sspi_log("Using 'Conf' layer\n");
0990                 obuf[0] = 4;
0991             } else if (use_int) {
0992                 sspi_log("Using 'Int' layer\n");
0993                 obuf[0] = 2;
0994             } else {
0995                 sspi_log("Using 'None' layer\n");
0996                 obuf[0] = 1;
0997             }
0998 
0999             // as far as i can tell, there is no max decrypt size
1000             //   with sspi.  so we'll just pick some number.
1001             //   a small one is good, to prevent excessive input
1002             //   buffering.
1003             // in other parts of the code, it is assumed this
1004             //   value is less than INT_MAX
1005             int _max_dec_size = 8192; // same as cyrus
1006 
1007             // max size must be zero if no security layer is used
1008             if (!use_conf && !use_int)
1009                 _max_dec_size = 0;
1010 
1011             obuf[1] = (unsigned char)((_max_dec_size >> 16) & 0xff);
1012             obuf[2] = (unsigned char)((_max_dec_size >> 8) & 0xff);
1013             obuf[3] = (unsigned char)((_max_dec_size)&0xff);
1014 
1015             if (!authzid.isEmpty())
1016                 obuf += authzid.toUtf8();
1017 
1018             str.clear();
1019             for (int n = 0; n < obuf.size(); ++n)
1020                 str += QString().sprintf("%02x", (unsigned int)obuf[n]);
1021             sspi_log(QString("Sending application token: [%1]\n").arg(str));
1022 
1023             if (sess.encode(obuf, out, false) != KerberosSession::Success) {
1024                 sspi_log("Error encoding application token\n");
1025                 return ErrorAppTokenEncode;
1026             }
1027 
1028             if (use_conf || use_int)
1029                 do_layer = true;
1030             if (use_conf)
1031                 do_conf = true;
1032 
1033             max_enc_size = _max_enc_size;
1034             max_dec_size = _max_dec_size;
1035 
1036             *authenticated = true;
1037         }
1038 
1039         return Success;
1040     }
1041 
1042     ReturnCode encode(const QByteArray &in, QByteArray *out)
1043     {
1044         if (!do_layer) {
1045             *out = in;
1046             return Success;
1047         }
1048 
1049         int local_encrypt_max;
1050         if (sess.get_max_encrypt_size(&local_encrypt_max) != KerberosSession::Success)
1051             return ErrorEncode;
1052 
1053         // send no more per-packet than what our local system will
1054         //   encrypt AND no more than what the peer will accept.
1055         int chunk_max = qMin(local_encrypt_max, max_enc_size);
1056         if (chunk_max < 8) {
1057             sspi_log("Error, chunk_max is ridiculously small\n");
1058             return ErrorEncode;
1059         }
1060 
1061         QByteArray total_out;
1062 
1063         // break up into packets, if input exceeds max size
1064         int encoded = 0;
1065         while (encoded < in.size()) {
1066             int        left       = in.size() - encoded;
1067             int        chunk_size = qMin(left, chunk_max);
1068             QByteArray kerb_in    = QByteArray::fromRawData(in.data() + encoded, chunk_size);
1069             QByteArray kerb_out;
1070             if (sess.encode(kerb_in, &kerb_out, do_conf) != KerberosSession::Success)
1071                 return ErrorEncode;
1072 
1073             QByteArray sasl_out(kerb_out.size() + 4, 0);
1074 
1075             // SASL (not GSS!) uses a 4 byte length prefix
1076             quint32 len = kerb_out.size();
1077             sasl_out[0] = (unsigned char)((len >> 24) & 0xff);
1078             sasl_out[1] = (unsigned char)((len >> 16) & 0xff);
1079             sasl_out[2] = (unsigned char)((len >> 8) & 0xff);
1080             sasl_out[3] = (unsigned char)((len)&0xff);
1081 
1082             memcpy(sasl_out.data() + 4, kerb_out.data(), kerb_out.size());
1083 
1084             encoded += kerb_in.size();
1085             total_out += sasl_out;
1086         }
1087 
1088         *out = total_out;
1089         return Success;
1090     }
1091 
1092     ReturnCode decode(const QByteArray &in, QByteArray *out)
1093     {
1094         if (!do_layer) {
1095             *out = in;
1096             return Success;
1097         }
1098 
1099         // buffer the input
1100         inbuf += in;
1101 
1102         QByteArray total_out;
1103 
1104         // the buffer might contain many packets.  decode as many
1105         //   as possible
1106         while (1) {
1107             if (inbuf.size() < 4) {
1108                 // need more data
1109                 break;
1110             }
1111 
1112             // SASL (not GSS!) uses a 4 byte length prefix
1113             quint32 ulen = 0;
1114             ulen += (unsigned char)inbuf[0];
1115             ulen <<= 8;
1116             ulen += (unsigned char)inbuf[1];
1117             ulen <<= 8;
1118             ulen += (unsigned char)inbuf[2];
1119             ulen <<= 8;
1120             ulen += (unsigned char)inbuf[3];
1121 
1122             // this capping is safe, because we throw error if the value
1123             //   is too large, and an acceptable value will always be
1124             //   lower than the maximum integer size.
1125             int len = cap_to_int<quint32>(ulen);
1126             if (len > max_dec_size) {
1127                 // this means the peer ignored our max buffer size.
1128                 //   very evil, or we're under attack.
1129                 sspi_log("Error, decode size too large\n");
1130                 return ErrorDecodeTooLarge;
1131             }
1132 
1133             if (inbuf.size() - 4 < len) {
1134                 // need more data
1135                 break;
1136             }
1137 
1138             // take the packet from the inbuf
1139             QByteArray kerb_in = inbuf.mid(4, len);
1140             memmove(inbuf.data(), inbuf.data() + len + 4, inbuf.size() - len - 4);
1141             inbuf.resize(inbuf.size() - len - 4);
1142 
1143             // count incomplete packets as errors, since they are sasl framed
1144             QByteArray kerb_out;
1145             bool       encrypted;
1146             if (sess.decode(kerb_in, &kerb_out, &encrypted) != KerberosSession::Success)
1147                 return ErrorDecode;
1148 
1149             if (do_conf && !encrypted) {
1150                 sspi_log("Error, received unencrypted packet in 'Conf' mode\n");
1151                 return ErrorDecodeNotEncrypted;
1152             }
1153 
1154             total_out += kerb_out;
1155         }
1156 
1157         *out = total_out;
1158         return Success;
1159     }
1160 };
1161 
1162 //----------------------------------------------------------------------------
1163 // SaslWinGss
1164 //----------------------------------------------------------------------------
1165 class SaslWinGss : public SASLContext
1166 {
1167     Q_OBJECT
1168 
1169 public:
1170     SaslGssapiSession  *sess;
1171     bool                authed;
1172     Result              _result;
1173     SASL::AuthCondition _authCondition;
1174     QByteArray          _step_to_net;
1175     QByteArray          _to_net, _to_app;
1176     int                 enc;
1177     SafeTimer           resultsReadyTrigger;
1178 
1179     QString opt_service, opt_host, opt_ext_id;
1180     int     opt_ext_ssf;
1181     int     opt_flags;
1182     int     opt_minssf, opt_maxssf;
1183 
1184     QString opt_authzid;
1185 
1186     SaslWinGss(Provider *p)
1187         : SASLContext(p)
1188         , sess(0)
1189         , resultsReadyTrigger(this)
1190     {
1191         connect(&resultsReadyTrigger, SIGNAL(timeout()), SIGNAL(resultsReady()));
1192         resultsReadyTrigger.setSingleShot(true);
1193     }
1194 
1195     Provider::Context *clone() const
1196     {
1197         return 0;
1198     }
1199 
1200     virtual void reset()
1201     {
1202         delete sess;
1203         sess   = 0;
1204         authed = false;
1205         _step_to_net.clear();
1206         _to_net.clear();
1207         _to_app.clear();
1208         resultsReadyTrigger.stop();
1209 
1210         opt_service.clear();
1211         opt_host.clear();
1212         opt_ext_id.clear();
1213         opt_authzid.clear();
1214     }
1215 
1216     virtual void setup(const QString  &service,
1217                        const QString  &host,
1218                        const HostPort *local,
1219                        const HostPort *remote,
1220                        const QString  &ext_id,
1221                        int             ext_ssf)
1222     {
1223         // unused by this provider
1224         Q_UNUSED(local);
1225         Q_UNUSED(remote);
1226 
1227         opt_service = service;
1228         opt_host    = host;
1229         opt_ext_id  = ext_id;
1230         opt_ext_ssf = ext_ssf;
1231     }
1232 
1233     virtual void setConstraints(SASL::AuthFlags f, int minSSF, int maxSSF)
1234     {
1235         opt_flags  = (int)f;
1236         opt_minssf = minSSF;
1237         opt_maxssf = maxSSF;
1238     }
1239 
1240     virtual void startClient(const QStringList &mechlist, bool allowClientSendFirst)
1241     {
1242         // we only support GSSAPI
1243         if (!mechlist.contains("GSSAPI")) {
1244             _result        = Error;
1245             _authCondition = SASL::NoMechanism;
1246             resultsReadyTrigger.start();
1247             return;
1248         }
1249 
1250         // GSSAPI (or this provider) doesn't meet these requirements
1251         if (opt_flags & SASL::RequireForwardSecrecy || opt_flags & SASL::RequirePassCredentials ||
1252             !allowClientSendFirst) {
1253             _result        = Error;
1254             _authCondition = SASL::NoMechanism;
1255             resultsReadyTrigger.start();
1256             return;
1257         }
1258 
1259         sess          = new SaslGssapiSession;
1260         sess->authzid = opt_authzid;
1261 
1262         int secflags = 0;
1263         if (opt_minssf > 1)
1264             secflags |= SaslGssapiSession::RequireConf;
1265         else if (opt_minssf == 1)
1266             secflags |= SaslGssapiSession::RequireAtLeastInt;
1267 
1268         SaslGssapiSession::ReturnCode ret = sess->init(opt_service, opt_host, secflags);
1269         if (ret != SaslGssapiSession::Success) {
1270             _result        = Error;
1271             _authCondition = SASL::AuthFail;
1272             resultsReadyTrigger.start();
1273             return;
1274         }
1275 
1276         ret = sess->step(QByteArray(), &_step_to_net, &authed);
1277         if (ret != SaslGssapiSession::Success) {
1278             _result        = Error;
1279             _authCondition = SASL::AuthFail;
1280             resultsReadyTrigger.start();
1281             return;
1282         }
1283 
1284         if (authed)
1285             _result = Success;
1286         else
1287             _result = Continue;
1288 
1289         resultsReadyTrigger.start();
1290     }
1291 
1292     virtual void startServer(const QString &realm, bool disableServerSendLast)
1293     {
1294         // server mode unsupported at this time
1295         Q_UNUSED(realm);
1296         Q_UNUSED(disableServerSendLast);
1297 
1298         _result        = Error;
1299         _authCondition = SASL::AuthFail;
1300         resultsReadyTrigger.start();
1301     }
1302 
1303     virtual void serverFirstStep(const QString &mech, const QByteArray *clientInit)
1304     {
1305         // server mode unsupported at this time
1306         Q_UNUSED(mech);
1307         Q_UNUSED(clientInit);
1308     }
1309 
1310     virtual void nextStep(const QByteArray &from_net)
1311     {
1312         SaslGssapiSession::ReturnCode ret = sess->step(from_net, &_step_to_net, &authed);
1313         if (ret != SaslGssapiSession::Success) {
1314             _result        = Error;
1315             _authCondition = SASL::AuthFail;
1316             resultsReadyTrigger.start();
1317             return;
1318         }
1319 
1320         if (authed)
1321             _result = Success;
1322         else
1323             _result = Continue;
1324 
1325         resultsReadyTrigger.start();
1326     }
1327 
1328     virtual void tryAgain()
1329     {
1330         // we never ask for params, so this function should never be
1331         //   called
1332     }
1333 
1334     virtual void update(const QByteArray &from_net, const QByteArray &from_app)
1335     {
1336         SaslGssapiSession::ReturnCode ret;
1337         QByteArray                    a;
1338 
1339         if (!from_net.isEmpty()) {
1340             ret = sess->decode(from_net, &a);
1341             if (ret != SaslGssapiSession::Success) {
1342                 _result = Error;
1343                 resultsReadyTrigger.start();
1344                 return;
1345             }
1346 
1347             _to_app += a;
1348         }
1349 
1350         if (!from_app.isEmpty()) {
1351             ret = sess->encode(from_app, &a);
1352             if (ret != SaslGssapiSession::Success) {
1353                 _result = Error;
1354                 resultsReadyTrigger.start();
1355                 return;
1356             }
1357 
1358             _to_net += a;
1359             enc += from_app.size();
1360         }
1361 
1362         _result = Success;
1363         resultsReadyTrigger.start();
1364     }
1365 
1366     virtual bool waitForResultsReady(int msecs)
1367     {
1368         // all results are ready instantly
1369         Q_UNUSED(msecs);
1370         resultsReadyTrigger.stop();
1371         return true;
1372     }
1373 
1374     virtual Result result() const
1375     {
1376         return _result;
1377     }
1378 
1379     virtual QStringList mechlist() const
1380     {
1381         // server mode unsupported at this time
1382         return QStringList();
1383     }
1384 
1385     virtual QString mech() const
1386     {
1387         // only mech we support :)
1388         return "GSSAPI";
1389     }
1390 
1391     virtual bool haveClientInit() const
1392     {
1393         // GSSAPI always has a client init response
1394         return true;
1395     }
1396 
1397     virtual QByteArray stepData() const
1398     {
1399         return _step_to_net;
1400     }
1401 
1402     virtual QByteArray to_net()
1403     {
1404         QByteArray a = _to_net;
1405         _to_net.clear();
1406         enc = 0;
1407         return a;
1408     }
1409 
1410     virtual int encoded() const
1411     {
1412         return enc;
1413     }
1414 
1415     virtual QByteArray to_app()
1416     {
1417         QByteArray a = _to_app;
1418         _to_app.clear();
1419         return a;
1420     }
1421 
1422     virtual int ssf() const
1423     {
1424         if (!sess->do_layer)
1425             return 0;
1426 
1427         if (sess->do_conf) {
1428             // TODO: calculate this value somehow?  for now we'll
1429             //   just hard code it to 56, which is basically what
1430             //   cyrus does.
1431             return 56;
1432         } else
1433             return 1;
1434     }
1435 
1436     virtual SASL::AuthCondition authCondition() const
1437     {
1438         return _authCondition;
1439     }
1440 
1441     virtual SASL::Params clientParams() const
1442     {
1443         // we never ask for params
1444         return SASL::Params();
1445     }
1446 
1447     virtual void
1448     setClientParams(const QString *user, const QString *authzid, const SecureArray *pass, const QString *realm)
1449     {
1450         // unused by this provider
1451         Q_UNUSED(user);
1452         Q_UNUSED(pass);
1453         Q_UNUSED(realm);
1454 
1455         if (authzid) {
1456             opt_authzid = *authzid;
1457             if (sess)
1458                 sess->authzid = opt_authzid;
1459         } else {
1460             opt_authzid.clear();
1461             if (sess)
1462                 sess->authzid.clear();
1463         }
1464     }
1465 
1466     virtual QStringList realmlist() const
1467     {
1468         // unused by this provider
1469         return QStringList();
1470     }
1471 
1472     virtual QString username() const
1473     {
1474         // server mode unsupported at this time
1475         return QString();
1476     }
1477 
1478     virtual QString authzid() const
1479     {
1480         // server mode unsupported at this time
1481         return QString();
1482     }
1483 };
1484 
1485 #endif // !defined(FORWARD_ONLY)
1486 
1487 //----------------------------------------------------------------------------
1488 // MetaSasl
1489 //----------------------------------------------------------------------------
1490 #ifndef FORWARD_ONLY
1491 class wingssProvider;
1492 static bool wingssProvider_have_sspi(wingssProvider *provider);
1493 #endif
1494 
1495 class MetaSasl : public SASLContext
1496 {
1497     Q_OBJECT
1498 
1499 public:
1500     SASLContext *s;
1501 
1502     Result              _result;
1503     SASL::AuthCondition _authCondition;
1504     SafeTimer           resultsReadyTrigger;
1505     Synchronizer        sync;
1506     bool                waiting;
1507 
1508     QString         opt_service, opt_host;
1509     bool            have_opt_local, have_opt_remote;
1510     HostPort        opt_local, opt_remote;
1511     QString         opt_ext_id;
1512     int             opt_ext_ssf;
1513     SASL::AuthFlags opt_flags;
1514     int             opt_minssf, opt_maxssf;
1515 
1516     bool        have_opt_user, have_opt_authzid, have_opt_pass, have_opt_realm;
1517     QString     opt_user, opt_authzid, opt_realm;
1518     SecureArray opt_pass;
1519 
1520     class SaslProvider
1521     {
1522     public:
1523         SASLContext *sasl;
1524         bool         ready;
1525         QStringList  mechlist;
1526 
1527         SaslProvider()
1528             : sasl(0)
1529             , ready(false)
1530         {
1531         }
1532     };
1533 
1534     QList<SaslProvider> saslProviders;
1535     bool                serverInit_active;
1536     Result              serverInit_result;
1537     QStringList         serverInit_mechlist;
1538 
1539     MetaSasl(Provider *p)
1540         : SASLContext(p)
1541         , resultsReadyTrigger(this)
1542         , sync(this)
1543         , waiting(false)
1544         , serverInit_active(false)
1545     {
1546         s = 0;
1547 
1548         have_opt_user    = false;
1549         have_opt_authzid = false;
1550         have_opt_pass    = false;
1551         have_opt_realm   = false;
1552 
1553         connect(&resultsReadyTrigger, SIGNAL(timeout()), SIGNAL(resultsReady()));
1554         resultsReadyTrigger.setSingleShot(true);
1555     }
1556 
1557     ~MetaSasl()
1558     {
1559         delete s;
1560     }
1561 
1562     virtual Provider::Context *clone() const
1563     {
1564         return 0;
1565     }
1566 
1567     void clearSaslProviders()
1568     {
1569         foreach (const SaslProvider &sp, saslProviders)
1570             delete sp.sasl;
1571 
1572         saslProviders.clear();
1573     }
1574 
1575     virtual void reset()
1576     {
1577         delete s;
1578         s = 0;
1579 
1580         resultsReadyTrigger.stop();
1581 
1582         opt_service.clear();
1583         opt_host.clear();
1584         opt_ext_id.clear();
1585         opt_user.clear();
1586         opt_authzid.clear();
1587         opt_realm.clear();
1588         opt_pass.clear();
1589 
1590         have_opt_user    = false;
1591         have_opt_authzid = false;
1592         have_opt_pass    = false;
1593         have_opt_realm   = false;
1594 
1595         clearSaslProviders();
1596         serverInit_active = false;
1597         serverInit_mechlist.clear();
1598     }
1599 
1600     virtual void setup(const QString  &service,
1601                        const QString  &host,
1602                        const HostPort *local,
1603                        const HostPort *remote,
1604                        const QString  &ext_id,
1605                        int             ext_ssf)
1606     {
1607         opt_service     = service;
1608         opt_host        = host;
1609         have_opt_local  = false;
1610         have_opt_remote = false;
1611         if (local) {
1612             have_opt_local = true;
1613             opt_local      = *local;
1614         }
1615         if (remote) {
1616             have_opt_remote = true;
1617             opt_remote      = *remote;
1618         }
1619         opt_ext_id  = ext_id;
1620         opt_ext_ssf = ext_ssf;
1621     }
1622 
1623     virtual void setConstraints(SASL::AuthFlags f, int minSSF, int maxSSF)
1624     {
1625         opt_flags  = f;
1626         opt_minssf = minSSF;
1627         opt_maxssf = maxSSF;
1628     }
1629 
1630     virtual void startClient(const QStringList &mechlist, bool allowClientSendFirst)
1631     {
1632 #ifndef FORWARD_ONLY
1633         if (mechlist.contains("GSSAPI") && wingssProvider_have_sspi((wingssProvider *)provider())) {
1634             s = new SaslWinGss(provider());
1635         } else {
1636 #endif
1637             // collect providers supporting sasl, in priority order.
1638             //   (note: providers() is in priority order already)
1639             ProviderList list;
1640             foreach (Provider *p, providers()) {
1641                 QString name = p->name();
1642 
1643                 // skip ourself
1644                 if (name == PROVIDER_NAME)
1645                     continue;
1646 
1647                 if (p->features().contains("sasl")) {
1648                     // FIXME: improve qca so this isn't needed
1649                     SASL tmp_object_to_cause_plugin_init(0, name);
1650 
1651                     // add to the list
1652                     list += p;
1653                 }
1654             }
1655 
1656             if (!list.isEmpty()) {
1657                 // use the first
1658                 s = static_cast<SASLContext *>(list.first()->createContext("sasl"));
1659             }
1660 #ifndef FORWARD_ONLY
1661         }
1662 #endif
1663 
1664         if (!s) {
1665             // no usable provider?  throw error
1666             _result        = Error;
1667             _authCondition = SASL::NoMechanism;
1668             resultsReadyTrigger.start();
1669             return;
1670         }
1671 
1672         // proper parenting
1673         s->setParent(this);
1674 
1675         const HostPort *pLocal  = 0;
1676         const HostPort *pRemote = 0;
1677         if (have_opt_local)
1678             pLocal = &opt_local;
1679         if (have_opt_remote)
1680             pRemote = &opt_remote;
1681         s->setup(opt_service, opt_host, pLocal, pRemote, opt_ext_id, opt_ext_ssf);
1682         s->setConstraints(opt_flags, opt_minssf, opt_maxssf);
1683 
1684         const QString     *pUser    = 0;
1685         const QString     *pAuthzid = 0;
1686         const SecureArray *pPass    = 0;
1687         const QString     *pRealm   = 0;
1688         if (have_opt_user)
1689             pUser = &opt_user;
1690         if (have_opt_authzid)
1691             pAuthzid = &opt_authzid;
1692         if (have_opt_pass)
1693             pPass = &opt_pass;
1694         if (have_opt_realm)
1695             pRealm = &opt_realm;
1696         s->setClientParams(pUser, pAuthzid, pPass, pRealm);
1697         connect(s, SIGNAL(resultsReady()), SLOT(s_resultsReady()));
1698 
1699         QString str = QString("MetaSasl: client using %1 with %2 mechs")
1700                           .arg(s->provider()->name(), QString::number(mechlist.count()));
1701         QCA_logTextMessage(str, Logger::Debug);
1702         s->startClient(mechlist, allowClientSendFirst);
1703     }
1704 
1705     virtual void startServer(const QString &realm, bool disableServerSendLast)
1706     {
1707         // collect mechs of all providers, by starting all of them
1708         serverInit_active = true;
1709 
1710         ProviderList list;
1711         foreach (Provider *p, providers()) {
1712             QString name = p->name();
1713 
1714             // skip ourself
1715             if (name == PROVIDER_NAME)
1716                 continue;
1717 
1718             if (p->features().contains("sasl")) {
1719                 // FIXME: improve qca so this isn't needed
1720                 SASL tmp_object_to_cause_plugin_init(0, name);
1721 
1722                 // add to the list
1723                 list += p;
1724             }
1725         }
1726 
1727         foreach (Provider *p, list) {
1728             SaslProvider sp;
1729 
1730             sp.sasl = static_cast<SASLContext *>(p->createContext("sasl"));
1731 
1732             // proper parenting
1733             sp.sasl->setParent(this);
1734 
1735             const HostPort *pLocal  = 0;
1736             const HostPort *pRemote = 0;
1737             if (have_opt_local)
1738                 pLocal = &opt_local;
1739             if (have_opt_remote)
1740                 pRemote = &opt_remote;
1741             sp.sasl->setup(opt_service, opt_host, pLocal, pRemote, opt_ext_id, opt_ext_ssf);
1742             sp.sasl->setConstraints(opt_flags, opt_minssf, opt_maxssf);
1743             connect(sp.sasl, SIGNAL(resultsReady()), SLOT(serverInit_resultsReady()));
1744 
1745             saslProviders += sp;
1746 
1747             sp.sasl->startServer(realm, disableServerSendLast);
1748         }
1749     }
1750 
1751     virtual void serverFirstStep(const QString &mech, const QByteArray *clientInit)
1752     {
1753         // choose a provider based on the mech
1754         int at = choose_provider(mech);
1755 
1756         // extract it and clean up the rest
1757         SASLContext *sasl = saslProviders[at].sasl;
1758         sasl->disconnect(this);
1759         saslProviders.removeAt(at);
1760         clearSaslProviders();
1761         serverInit_active = false;
1762 
1763         // use the chosen provider
1764         s = sasl;
1765         connect(s, SIGNAL(resultsReady()), SLOT(s_resultsReady()));
1766         s->serverFirstStep(mech, clientInit);
1767     }
1768 
1769     virtual void nextStep(const QByteArray &from_net)
1770     {
1771         s->nextStep(from_net);
1772     }
1773 
1774     virtual void tryAgain()
1775     {
1776         s->tryAgain();
1777     }
1778 
1779     virtual void update(const QByteArray &from_net, const QByteArray &from_app)
1780     {
1781         s->update(from_net, from_app);
1782     }
1783 
1784     virtual bool waitForResultsReady(int msecs)
1785     {
1786         if (serverInit_active) {
1787             waiting  = true;
1788             bool ret = sync.waitForCondition(msecs);
1789             waiting  = false;
1790             return ret;
1791         } else if (s)
1792             return s->waitForResultsReady(msecs);
1793         else
1794             return true;
1795     }
1796 
1797     virtual Result result() const
1798     {
1799         if (serverInit_active)
1800             return serverInit_result;
1801         else if (s)
1802             return s->result();
1803         else
1804             return _result;
1805     }
1806 
1807     virtual QStringList mechlist() const
1808     {
1809         return serverInit_mechlist;
1810     }
1811 
1812     virtual QString mech() const
1813     {
1814         if (s)
1815             return s->mech();
1816         else
1817             return QString();
1818     }
1819 
1820     virtual bool haveClientInit() const
1821     {
1822         return s->haveClientInit();
1823     }
1824 
1825     virtual QByteArray stepData() const
1826     {
1827         return s->stepData();
1828     }
1829 
1830     virtual QByteArray to_net()
1831     {
1832         return s->to_net();
1833     }
1834 
1835     virtual int encoded() const
1836     {
1837         return s->encoded();
1838     }
1839 
1840     virtual QByteArray to_app()
1841     {
1842         return s->to_app();
1843     }
1844 
1845     virtual int ssf() const
1846     {
1847         return s->ssf();
1848     }
1849 
1850     virtual SASL::AuthCondition authCondition() const
1851     {
1852         if (s)
1853             return s->authCondition();
1854         else
1855             return _authCondition;
1856     }
1857 
1858     virtual SASL::Params clientParams() const
1859     {
1860         return s->clientParams();
1861     }
1862 
1863     virtual void
1864     setClientParams(const QString *user, const QString *authzid, const SecureArray *pass, const QString *realm)
1865     {
1866         if (!s) {
1867             if (user) {
1868                 have_opt_user = true;
1869                 opt_user      = *user;
1870             }
1871             if (authzid) {
1872                 have_opt_authzid = true;
1873                 opt_authzid      = *authzid;
1874             }
1875             if (pass) {
1876                 have_opt_pass = true;
1877                 opt_pass      = *pass;
1878             }
1879             if (realm) {
1880                 have_opt_realm = true;
1881                 opt_realm      = *realm;
1882             }
1883         } else {
1884             s->setClientParams(user, authzid, pass, realm);
1885         }
1886     }
1887 
1888     virtual QStringList realmlist() const
1889     {
1890         return s->realmlist();
1891     }
1892 
1893     virtual QString username() const
1894     {
1895         return s->username();
1896     }
1897 
1898     virtual QString authzid() const
1899     {
1900         return s->authzid();
1901     }
1902 
1903 private Q_SLOTS:
1904     void s_resultsReady()
1905     {
1906         emit resultsReady();
1907     }
1908 
1909     void serverInit_resultsReady()
1910     {
1911         SASLContext *sasl = (SASLContext *)sender();
1912 
1913         int at = -1;
1914         for (int n = 0; n < saslProviders.count(); ++n) {
1915             if (saslProviders[n].sasl == sasl) {
1916                 at = n;
1917                 break;
1918             }
1919         }
1920         if (at == -1)
1921             return;
1922 
1923         if (sasl->result() == Success) {
1924             saslProviders[at].ready    = true;
1925             saslProviders[at].mechlist = sasl->mechlist();
1926 
1927             bool allReady = true;
1928             for (int n = 0; n < saslProviders.count(); ++n) {
1929                 if (!saslProviders[n].ready) {
1930                     allReady = false;
1931                     break;
1932                 }
1933             }
1934 
1935             if (allReady) {
1936                 // indicate success
1937                 serverInit_result   = Success;
1938                 serverInit_mechlist = combine_mechlists();
1939 
1940                 if (waiting)
1941                     sync.conditionMet();
1942                 else
1943                     emit resultsReady();
1944             }
1945         } else {
1946             delete sasl;
1947             saslProviders.removeAt(at);
1948 
1949             if (saslProviders.isEmpty()) {
1950                 // indicate error
1951                 serverInit_result = Error;
1952                 _authCondition    = SASL::NoMechanism;
1953 
1954                 if (waiting)
1955                     sync.conditionMet();
1956                 else
1957                     emit resultsReady();
1958             }
1959         }
1960     }
1961 
1962 private:
1963     QStringList combine_mechlists()
1964     {
1965         QStringList out;
1966 
1967         // FIXME: consider prioritizing certain mechs?
1968         foreach (const SaslProvider &sp, saslProviders) {
1969             foreach (const QString &mech, sp.mechlist) {
1970                 if (!out.contains(mech))
1971                     out += mech;
1972             }
1973         }
1974 
1975         return out;
1976     }
1977 
1978     int choose_provider(const QString &mech)
1979     {
1980         int at = -1;
1981 
1982         // find a provider for this mech
1983         for (int n = 0; n < saslProviders.count(); ++n) {
1984             const SaslProvider &sp = saslProviders[n];
1985             if (sp.mechlist.contains(mech)) {
1986                 at = n;
1987                 break;
1988             }
1989         }
1990 
1991         // no provider offered this mech?  then just go with the
1992         //   first provider
1993         if (at == -1)
1994             at = 0;
1995 
1996         return at;
1997     }
1998 };
1999 
2000 class wingssProvider : public Provider
2001 {
2002 public:
2003     mutable QMutex m;
2004     mutable bool   forced_priority;
2005     bool           have_sspi;
2006 
2007     wingssProvider()
2008         : forced_priority(false)
2009         , have_sspi(false)
2010     {
2011     }
2012 
2013     virtual void init()
2014     {
2015 #ifndef FORWARD_ONLY
2016         sspi_set_logger(do_log);
2017         have_sspi = sspi_load();
2018 #endif
2019     }
2020 
2021     ~wingssProvider()
2022     {
2023 #ifndef FORWARD_ONLY
2024         if (have_sspi)
2025             sspi_unload();
2026 #endif
2027     }
2028 
2029     virtual int qcaVersion() const
2030     {
2031         return QCA_VERSION;
2032     }
2033 
2034     virtual QString name() const
2035     {
2036         return PROVIDER_NAME;
2037     }
2038 
2039     virtual QStringList features() const
2040     {
2041         // due to context manipulation, this plugin is only designed
2042         //   for qca 2.0 at this time, and not a possible 2.1, etc.
2043         if ((qcaVersion() & 0xffff00) > 0x020000)
2044             return QStringList();
2045 
2046         m.lock();
2047         // FIXME: we need to prioritize this plugin to be higher
2048         //   than other plugins by default.  unfortunately there's
2049         //   no clean way to do this.  we can't change our priority
2050         //   until we are slotted into the qca provider system.  the
2051         //   constructor, qcaVersion, and name functions are all
2052         //   guaranteed to be called, but unfortunately they are
2053         //   only guaranteed to be called before the slotting.  the
2054         //   features function is essentially guaranteed to be called
2055         //   after the slotting though, since QCA::isSupported()
2056         //   trips it, and any proper QCA app will call isSupported.
2057         if (!forced_priority) {
2058             forced_priority = true;
2059             setProviderPriority(PROVIDER_NAME, 0);
2060         }
2061         m.unlock();
2062 
2063         QStringList list;
2064         list += "sasl";
2065         return list;
2066     }
2067 
2068     virtual Context *createContext(const QString &type)
2069     {
2070         if (type == "sasl")
2071             return new MetaSasl(this);
2072         else
2073             return 0;
2074     }
2075 
2076 #ifndef FORWARD_ONLY
2077     static void do_log(const QString &str)
2078     {
2079         QCA_logTextMessage(str, Logger::Debug);
2080     }
2081 #endif
2082 };
2083 
2084 #ifndef FORWARD_ONLY
2085 bool wingssProvider_have_sspi(wingssProvider *provider)
2086 {
2087     return provider->have_sspi;
2088 }
2089 #endif
2090 
2091 }
2092 
2093 using namespace wingssQCAPlugin;
2094 
2095 //----------------------------------------------------------------------------
2096 // wingssPlugin
2097 //----------------------------------------------------------------------------
2098 
2099 class wingssPlugin : public QObject, public QCAPlugin
2100 {
2101     Q_OBJECT
2102     Q_PLUGIN_METADATA(IID "com.affinix.qca.Plugin/1.0")
2103     Q_INTERFACES(QCAPlugin)
2104 
2105 public:
2106     virtual Provider *createProvider()
2107     {
2108         return new wingssProvider;
2109     }
2110 };
2111 
2112 #include "qca-wingss.moc"