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"