Skip to content

Commit 6dc2f3e

Browse files
committed
Refactor WLAN PSK retrieval code
- Reduce platform specific code in ImageWriter class, and move that to seperate classes. - Use API calls to get current SSID on Windows and Linux instead of launching command line utilities.
1 parent ebaf2ef commit 6dc2f3e

12 files changed

+372
-196
lines changed

src/CMakeLists.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ OPTION (ENABLE_TELEMETRY "Enable sending telemetry" ON)
1212
project(rpi-imager LANGUAGES CXX C)
1313
set(IMAGER_VERSION_MAJOR 1)
1414
set(IMAGER_VERSION_MINOR 7)
15-
set(IMAGER_VERSION_STR "${IMAGER_VERSION_MAJOR}.${IMAGER_VERSION_MINOR}.4")
16-
set(IMAGER_VERSION_CSV "${IMAGER_VERSION_MAJOR},${IMAGER_VERSION_MINOR},4,0")
15+
set(IMAGER_VERSION_STR "${IMAGER_VERSION_MAJOR}.${IMAGER_VERSION_MINOR}.4.1")
16+
set(IMAGER_VERSION_CSV "${IMAGER_VERSION_MAJOR},${IMAGER_VERSION_MINOR},4,1")
1717
add_definitions(-DIMAGER_VERSION_STR="${IMAGER_VERSION_STR}")
1818
add_definitions(-DIMAGER_VERSION_CSV=${IMAGER_VERSION_CSV})
1919

@@ -23,21 +23,21 @@ set(CMAKE_AUTORCC ON)
2323

2424
# Adding headers explicity so they are displayed in Qt Creator
2525
set(HEADERS config.h imagewriter.h networkaccessmanagerfactory.h nan.h drivelistitem.h drivelistmodel.h drivelistmodelpollthread.h driveformatthread.h powersaveblocker.h cli.h
26-
devicewrapper.h devicewrapperblockcacheentry.h devicewrapperpartition.h devicewrapperstructs.h devicewrapperfatpartition.h
26+
devicewrapper.h devicewrapperblockcacheentry.h devicewrapperpartition.h devicewrapperstructs.h devicewrapperfatpartition.h wlancredentials.h
2727
downloadthread.h downloadextractthread.h localfileextractthread.h downloadstatstelemetry.h dependencies/mountutils/src/mountutils.hpp dependencies/sha256crypt/sha256crypt.h)
2828

2929
# Add dependencies
3030
if (APPLE)
3131
set_source_files_properties("icons/rpi-imager.icns" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
3232
set(DEPENDENCIES acceleratedcryptographichash.cpp mac/macfile.cpp mac/macfile.h dependencies/mountutils/src/darwin/functions.cpp
33+
mac/macwlancredentials.h mac/macwlancredentials.cpp
3334
dependencies/drivelist/src/darwin/list.mm dependencies/drivelist/src/darwin/REDiskList.m icons/rpi-imager.icns)
3435
enable_language(OBJC C)
3536
elseif (UNIX)
36-
set(DEPENDENCIES dependencies/mountutils/src/linux/functions.cpp linux/linuxdrivelist.cpp)
37+
set(DEPENDENCIES dependencies/mountutils/src/linux/functions.cpp linux/linuxdrivelist.cpp linux/networkmanagerapi.h linux/networkmanagerapi.cpp)
3738
find_package(Qt5DBus)
3839
if(Qt5DBus_FOUND)
39-
set(DEPENDENCIES ${DEPENDENCIES} linux/udisks2api.cpp linux/udisks2api.h
40-
linux/networkmanagerapi.h linux/networkmanagerapi.cpp)
40+
set(DEPENDENCIES ${DEPENDENCIES} linux/udisks2api.cpp linux/udisks2api.h)
4141
set(EXTRALIBS Qt5::DBus)
4242
message("udisks2 support enabled")
4343
else()
@@ -62,7 +62,7 @@ elseif (UNIX)
6262
endif()
6363
elseif (WIN32)
6464
set(DEPENDENCIES acceleratedcryptographichash.cpp dependencies/mountutils/src/windows/functions.cpp dependencies/drivelist/src/windows/list.cpp
65-
windows/winfile.cpp windows/winfile.h
65+
windows/winfile.cpp windows/winfile.h windows/winwlancredentials.h windows/winwlancredentials.cpp
6666
windows/rpi-imager.rc)
6767
find_package(Qt5WinExtras REQUIRED)
6868
set(EXTRALIBS setupapi wlanapi Qt5::WinExtras)

src/OptionsPopup.qml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ Popup {
499499
fieldWifiCountry.currentIndex = fieldWifiCountry.find("GB")
500500
fieldWifiSSID.text = imageWriter.getSSID()
501501
if (fieldWifiSSID.text.length) {
502-
fieldWifiPassword.text = imageWriter.getPSK(fieldWifiSSID.text)
502+
fieldWifiPassword.text = imageWriter.getPSK()
503503
if (fieldWifiPassword.text.length) {
504504
chkShowPassword.checked = false
505505
if (Qt.platform.os == "osx") {

src/imagewriter.cpp

Lines changed: 10 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "driveformatthread.h"
1212
#include "localfileextractthread.h"
1313
#include "downloadstatstelemetry.h"
14+
#include "wlancredentials.h"
1415
#include <archive.h>
1516
#include <archive_entry.h>
1617
#include <random>
@@ -38,17 +39,10 @@
3839
#endif
3940
#ifdef Q_OS_DARWIN
4041
#include <QMessageBox>
41-
#include <security/security.h>
4242
#endif
4343

4444
#ifdef Q_OS_WIN
4545
#include <windows.h>
46-
#include <winioctl.h>
47-
#include <wlanapi.h>
48-
#ifndef WLAN_PROFILE_GET_PLAINTEXT_KEY
49-
#define WLAN_PROFILE_GET_PLAINTEXT_KEY 4
50-
#endif
51-
5246
#include <QWinTaskbarButton>
5347
#include <QWinTaskbarProgress>
5448
#endif
@@ -57,12 +51,6 @@
5751
#include <QtPlatformHeaders/QEglFSFunctions>
5852
#endif
5953

60-
#ifdef Q_OS_LINUX
61-
#ifndef QT_NO_DBUS
62-
#include "linux/networkmanagerapi.h"
63-
#endif
64-
#endif
65-
6654
ImageWriter::ImageWriter(QObject *parent)
6755
: QObject(parent), _repo(QUrl(QString(OSLIST_URL))), _dlnow(0), _verifynow(0),
6856
_engine(nullptr), _thread(nullptr), _verifyEnabled(false), _cachingEnabled(false),
@@ -863,173 +851,23 @@ QStringList ImageWriter::getKeymapLayoutList()
863851

864852
QString ImageWriter::getSSID()
865853
{
866-
/* Qt used to have proper bearer management that was able to provide a list of
867-
SSIDs, but since they retired it, resort to calling platform specific tools for now.
868-
Minimal implementation that only gets the currently connected SSID */
869-
870-
QString program, regexpstr, ssid;
871-
QStringList args;
872-
QProcess proc;
873-
874-
#ifdef Q_OS_WIN
875-
program = "netsh";
876-
args << "wlan" << "show" << "interfaces";
877-
regexpstr = "[ \t]+SSID[ \t]*: (.+)";
878-
#else
879-
#ifdef Q_OS_DARWIN
880-
program = "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport";
881-
args << "-I";
882-
regexpstr = "[ \t]+SSID: (.+)";
883-
#else
884-
program = "iwgetid";
885-
args << "-r";
886-
#endif
887-
#endif
888-
889-
proc.start(program, args);
890-
if (proc.waitForStarted(2000) && proc.waitForFinished(2000))
891-
{
892-
if (regexpstr.isEmpty())
893-
{
894-
ssid = proc.readAll().trimmed();
895-
}
896-
else
897-
{
898-
QRegularExpression rx(regexpstr);
899-
const QList<QByteArray> outputlines = proc.readAll().replace('\r', "").split('\n');
900-
901-
for (const QByteArray &line : outputlines) {
902-
QRegularExpressionMatch match = rx.match(line);
903-
if (match.hasMatch())
904-
{
905-
ssid = match.captured(1);
906-
break;
907-
}
908-
}
909-
}
910-
}
911-
912-
return ssid;
913-
}
914-
915-
inline QString unescapeXml(QString str)
916-
{
917-
static const char *table[] = {
918-
"&lt;", "<",
919-
"&gt;", ">",
920-
"&quot;", "\"",
921-
"&apos;", "'",
922-
"&amp;", "&"
923-
};
924-
int tableLen = sizeof(table) / sizeof(table[0]);
925-
926-
for (int i=0; i < tableLen; i+=2)
927-
{
928-
str.replace(table[i], table[i+1]);
929-
}
930-
931-
return str;
854+
return WlanCredentials::instance()->getSSID();
932855
}
933856

934-
QString ImageWriter::getPSK(const QString &ssid)
857+
QString ImageWriter::getPSK()
935858
{
936-
#ifdef Q_OS_WIN
937-
/* Windows allows retrieving wifi PSK */
938-
HANDLE h;
939-
DWORD ret = 0;
940-
DWORD supportedVersion = 0;
941-
DWORD clientVersion = 2;
942-
QString psk;
943-
944-
if (WlanOpenHandle(clientVersion, NULL, &supportedVersion, &h) != ERROR_SUCCESS)
945-
return QString();
946-
947-
PWLAN_INTERFACE_INFO_LIST ifList = NULL;
948-
949-
if (WlanEnumInterfaces(h, NULL, &ifList) == ERROR_SUCCESS)
950-
{
951-
for (int i=0; i < ifList->dwNumberOfItems; i++)
952-
{
953-
PWLAN_PROFILE_INFO_LIST profileList = NULL;
954-
955-
if (WlanGetProfileList(h, &ifList->InterfaceInfo[i].InterfaceGuid,
956-
NULL, &profileList) == ERROR_SUCCESS)
957-
{
958-
for (int j=0; j < profileList->dwNumberOfItems; j++)
959-
{
960-
QString s = QString::fromWCharArray(profileList->ProfileInfo[j].strProfileName);
961-
qDebug() << "Enumerating wifi profiles, SSID found:" << s << " looking for:" << ssid;
962-
963-
if (s == ssid) {
964-
DWORD flags = WLAN_PROFILE_GET_PLAINTEXT_KEY;
965-
DWORD access = 0;
966-
DWORD ret = 0;
967-
LPWSTR xmlstr = NULL;
968-
969-
if ( (ret = WlanGetProfile(h, &ifList->InterfaceInfo[i].InterfaceGuid, profileList->ProfileInfo[j].strProfileName,
970-
NULL, &xmlstr, &flags, &access)) == ERROR_SUCCESS && xmlstr)
971-
{
972-
QString xml = QString::fromWCharArray(xmlstr);
973-
qDebug() << "XML wifi profile:" << xml;
974-
QRegularExpression rx("<keyMaterial>(.+)</keyMaterial>");
975-
QRegularExpressionMatch match = rx.match(xml);
976-
977-
if (match.hasMatch()) {
978-
psk = unescapeXml(match.captured(1));
979-
}
980-
981-
WlanFreeMemory(xmlstr);
982-
break;
983-
}
984-
}
985-
}
986-
}
987-
988-
if (profileList) {
989-
WlanFreeMemory(profileList);
990-
}
991-
}
992-
}
993-
994-
if (ifList)
995-
WlanFreeMemory(ifList);
996-
WlanCloseHandle(h, NULL);
997-
998-
return psk;
999-
1000-
#else
1001859
#ifdef Q_OS_DARWIN
1002-
SecKeychainRef keychainRef;
1003-
QString psk;
1004-
QByteArray ssidAscii = ssid.toLatin1();
1005-
860+
/* On OSX the user is presented with a prompt for the admin password when opening the system key chain.
861+
* Ask if user wants to obtain the wlan password first to make sure this is desired and
862+
* to provide the user with context. */
1006863
if (QMessageBox::question(nullptr, "",
1007-
tr("Would you like to prefill the wifi password from the system keychain?")) == QMessageBox::Yes)
864+
tr("Would you like to prefill the wifi password from the system keychain?")) != QMessageBox::Yes)
1008865
{
1009-
if (SecKeychainOpen("/Library/Keychains/System.keychain", &keychainRef) == errSecSuccess)
1010-
{
1011-
UInt32 resultLen;
1012-
void *result;
1013-
if (SecKeychainFindGenericPassword(keychainRef, 0, NULL, ssidAscii.length(), ssidAscii.constData(), &resultLen, &result, NULL) == errSecSuccess)
1014-
{
1015-
psk = QByteArray((char *) result, resultLen);
1016-
SecKeychainItemFreeContent(NULL, result);
1017-
}
1018-
CFRelease(keychainRef);
1019-
}
866+
return QString();
1020867
}
1021-
1022-
return psk;
1023-
#else
1024-
#ifndef QT_NO_DBUS
1025-
NetworkManagerApi nm;
1026-
return nm.getPSK();
1027-
#else
1028-
Q_UNUSED(ssid)
1029-
return QString();
1030-
#endif
1031-
#endif
1032868
#endif
869+
870+
return WlanCredentials::instance()->getPSK();
1033871
}
1034872

1035873
bool ImageWriter::getBoolSetting(const QString &key)

src/imagewriter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class ImageWriter : public QObject
103103
Q_INVOKABLE QStringList getCountryList();
104104
Q_INVOKABLE QStringList getKeymapLayoutList();
105105
Q_INVOKABLE QString getSSID();
106-
Q_INVOKABLE QString getPSK(const QString &ssid);
106+
Q_INVOKABLE QString getPSK();
107107

108108
Q_INVOKABLE bool getBoolSetting(const QString &key);
109109
Q_INVOKABLE void setSetting(const QString &key, const QVariant &value);

src/linux/networkmanagerapi.cpp

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,76 @@
44
*/
55

66
#include "networkmanagerapi.h"
7+
#include <QDebug>
8+
#include <QNetworkInterface>
9+
#include <unistd.h>
10+
#include <sys/socket.h>
11+
#include <sys/types.h>
12+
#include <sys/ioctl.h>
13+
#include <linux/wireless.h>
14+
15+
#ifndef QT_NO_DBUS
716
#include <QDBusMetaType>
817
#include <QDBusInterface>
918
#include <QDBusReply>
10-
#include <QDebug>
19+
#endif
1120

1221
typedef QMap<QString, QVariantMap> VariantMapMap;
1322
Q_DECLARE_METATYPE(VariantMapMap)
1423

15-
NetworkManagerApi::NetworkManagerApi(QObject *parent)
16-
: QObject{parent}
24+
NetworkManagerApi::NetworkManagerApi()
1725
{
26+
#ifndef QT_NO_DBUS
27+
qDBusRegisterMetaType<VariantMapMap>();
28+
#endif
29+
}
1830

31+
QByteArray NetworkManagerApi::getSSID()
32+
{
33+
QByteArray ssid;
34+
const auto interfaces = QNetworkInterface::allInterfaces();
35+
36+
for (const auto &interface : interfaces)
37+
{
38+
if (interface.type() == interface.Wifi)
39+
{
40+
ssid = _getSSIDofInterface(interface.name().toLatin1());
41+
if (!ssid.isEmpty())
42+
break;
43+
}
44+
}
45+
46+
return ssid;
1947
}
2048

21-
QString NetworkManagerApi::getPSK()
49+
QByteArray NetworkManagerApi::_getSSIDofInterface(const QByteArray &iface)
2250
{
23-
qDBusRegisterMetaType<VariantMapMap>();
51+
char ssid[IW_ESSID_MAX_SIZE+1] = {0};
52+
struct iwreq iw = {0};
53+
int s = ::socket(AF_INET, SOCK_DGRAM, 0);
54+
55+
if (s == -1)
56+
return QByteArray();
57+
58+
strncpy(iw.ifr_ifrn.ifrn_name, iface.data(), sizeof(iw.ifr_ifrn.ifrn_name));
59+
iw.u.essid.pointer = ssid;
60+
iw.u.essid.length = IW_ESSID_MAX_SIZE;
61+
::ioctl(s, SIOCGIWESSID, &iw);
62+
::close(s);
63+
64+
return QByteArray(ssid);
65+
}
66+
67+
QByteArray NetworkManagerApi::getPSK()
68+
{
69+
#ifndef QT_NO_DBUS
2470
QDBusInterface nm("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager",
2571
"org.freedesktop.NetworkManager", QDBusConnection::systemBus());
2672

2773
if (!nm.isValid())
2874
{
2975
qDebug() << "NetworkManager not available";
30-
return QString();
76+
return QByteArray();
3177
}
3278

3379
const auto activeConnections = nm.property("ActiveConnections").value<QList<QDBusObjectPath>>();
@@ -52,8 +98,18 @@ QString NetworkManagerApi::getPSK()
5298
continue;
5399
QVariantMap secrets = reply.value().value("802-11-wireless-security");
54100
if (secrets.contains("psk"))
55-
return secrets.value("psk").toString();
101+
return secrets.value("psk").toByteArray();
56102
}
103+
#endif
104+
105+
return QByteArray();
106+
}
107+
108+
WlanCredentials *WlanCredentials::_instance = NULL;
109+
WlanCredentials *WlanCredentials::instance()
110+
{
111+
if (!_instance)
112+
_instance = new NetworkManagerApi();
57113

58-
return QString();
114+
return _instance;
59115
}

0 commit comments

Comments
 (0)