From 02b9033d50def3b4c70a4f0504b5894d4249be1b Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Mon, 7 Nov 2022 11:09:06 +0100 Subject: getProxyAuth: stop using deprecated API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new SDK marked several functions in SecKeychain family as deprecated. Fortunately for us, Security framework on the whole is not deprecated and thus provides an alternative API to inspect keychain items, and also much nicer one - instead of having a function with 15 parameters, those parameters are 'collapsed' into one, the 'query', which is a dictionary. Pick-to: 6.4 6.2 Fixes: QTBUG-108196 Change-Id: I602d1a846ff9683cac724859a776de2b901f5c1c Reviewed-by: MÃ¥rten Nordheim --- src/network/access/qnetworkaccessmanager.cpp | 106 ++++++++++++++++----------- 1 file changed, 64 insertions(+), 42 deletions(-) (limited to 'src/network/access') diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index d771485b2f..2e3a3d81f1 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -45,12 +45,15 @@ #include #include "QtCore/qapplicationstatic.h" +#include "QtCore/qloggingcategory.h" #include #if defined(Q_OS_MACOS) +#include + #include #include -#include +#include #endif #ifdef Q_OS_WASM #include "qnetworkreplywasmimpl_p.h" @@ -66,6 +69,8 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; +Q_LOGGING_CATEGORY(lcQnam, "qt.network.access.manager") + Q_APPLICATION_STATIC(QNetworkAccessFileBackendFactory, fileBackend) #ifdef QT_BUILD_INTERNAL @@ -77,55 +82,72 @@ Q_APPLICATION_STATIC(QFactoryLoader, loader, QNetworkAccessBackendFactory_iid, " #if defined(Q_OS_MACOS) bool getProxyAuth(const QString& proxyHostname, const QString &scheme, QString& username, QString& password) { - OSStatus err; - SecKeychainItemRef itemRef; - bool retValue = false; - SecProtocolType protocolType = kSecProtocolTypeAny; + CFStringRef protocolType = nullptr; if (scheme.compare("ftp"_L1, Qt::CaseInsensitive) == 0) { - protocolType = kSecProtocolTypeFTPProxy; + protocolType = kSecAttrProtocolFTPProxy; } else if (scheme.compare("http"_L1, Qt::CaseInsensitive) == 0 || scheme.compare("preconnect-http"_L1, Qt::CaseInsensitive) == 0) { - protocolType = kSecProtocolTypeHTTPProxy; + protocolType = kSecAttrProtocolHTTPProxy; } else if (scheme.compare("https"_L1,Qt::CaseInsensitive)==0 || scheme.compare("preconnect-https"_L1, Qt::CaseInsensitive) == 0) { - protocolType = kSecProtocolTypeHTTPSProxy; + protocolType = kSecAttrProtocolHTTPSProxy; + } else { + qCWarning(lcQnam) << "Cannot query user name and password for a proxy, unnknown protocol:" + << scheme; + return false; } - QByteArray proxyHostnameUtf8(proxyHostname.toUtf8()); - err = SecKeychainFindInternetPassword(NULL, - proxyHostnameUtf8.length(), proxyHostnameUtf8.constData(), - 0,NULL, - 0, NULL, - 0, NULL, - 0, - protocolType, - kSecAuthenticationTypeAny, - 0, NULL, - &itemRef); - if (err == noErr) { - - SecKeychainAttribute attr; - SecKeychainAttributeList attrList; - UInt32 length; - void *outData; - - attr.tag = kSecAccountItemAttr; - attr.length = 0; - attr.data = NULL; - - attrList.count = 1; - attrList.attr = &attr; - - if (SecKeychainItemCopyContent(itemRef, NULL, &attrList, &length, &outData) == noErr) { - username = QString::fromUtf8((const char*)attr.data, attr.length); - password = QString::fromUtf8((const char*)outData, length); - SecKeychainItemFreeContent(&attrList,outData); - retValue = true; - } - CFRelease(itemRef); + + QCFType query(CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, nullptr, nullptr)); + Q_ASSERT(query); + + CFDictionaryAddValue(query, kSecClass, kSecClassInternetPassword); + CFDictionaryAddValue(query, kSecAttrProtocol, protocolType); + + QCFType serverName; // Note the scope. + if (proxyHostname.size()) { + serverName = proxyHostname.toCFString(); + CFDictionaryAddValue(query, kSecAttrServer, serverName); + } + + // This is to get the user name in the result: + CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue); + // This one to get the password: + CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue); + + // The default for kSecMatchLimit key is 1 (the first match only), which is fine, + // so don't set this value explicitly. + + QCFType replyData; + if (SecItemCopyMatching(query, &replyData) != errSecSuccess) { + qCWarning(lcQnam, "Failed to extract user name and password from the keychain."); + return false; } - return retValue; + + if (!replyData || CFDictionaryGetTypeID() != CFGetTypeID(replyData)) { + qCWarning(lcQnam, "Query returned data in unexpected format."); + return false; + } + + CFDictionaryRef accountData = replyData.as(); + const void *value = CFDictionaryGetValue(accountData, kSecAttrAccount); + if (!value || CFGetTypeID(value) != CFStringGetTypeID()) { + qCWarning(lcQnam, "Cannot find user name or its format is unknown."); + return false; + } + username = QString::fromCFString(static_cast(value)); + + value = CFDictionaryGetValue(accountData, kSecValueData); + if (!value || CFGetTypeID(value) != CFDataGetTypeID()) { + qCWarning(lcQnam, "Cannot find password or its format is unknown."); + return false; + } + const CFDataRef passData = static_cast(value); + password = QString::fromLocal8Bit(reinterpret_cast(CFDataGetBytePtr(passData)), + qsizetype(CFDataGetLength(passData))); + return true; } -#endif +#endif // Q_OS_MACOS -- cgit v1.2.3