Changeset View
Changeset View
Standalone View
Standalone View
src/util/externalcommand.cpp
Show All 36 Lines | |||||
37 | #include <QtGlobal> | 37 | #include <QtGlobal> | ||
38 | #include <QStandardPaths> | 38 | #include <QStandardPaths> | ||
39 | #include <QString> | 39 | #include <QString> | ||
40 | #include <QStringList> | 40 | #include <QStringList> | ||
41 | #include <QTimer> | 41 | #include <QTimer> | ||
42 | #include <QThread> | 42 | #include <QThread> | ||
43 | #include <QVariant> | 43 | #include <QVariant> | ||
44 | 44 | | |||
45 | #include <QtCrypto> | | |||
46 | | ||||
47 | #include <KAuth> | 45 | #include <KAuth> | ||
48 | #include <KJob> | 46 | #include <KJob> | ||
49 | #include <KLocalizedString> | 47 | #include <KLocalizedString> | ||
50 | 48 | | |||
51 | struct ExternalCommandPrivate | 49 | struct ExternalCommandPrivate | ||
52 | { | 50 | { | ||
53 | Report *m_Report; | 51 | Report *m_Report; | ||
54 | QString m_Command; | 52 | QString m_Command; | ||
55 | QStringList m_Args; | 53 | QStringList m_Args; | ||
56 | int m_ExitCode; | 54 | int m_ExitCode; | ||
57 | QByteArray m_Output; | 55 | QByteArray m_Output; | ||
58 | QByteArray m_Input; | 56 | QByteArray m_Input; | ||
59 | DBusThread *m_thread; | 57 | DBusThread *m_thread; | ||
60 | QProcess::ProcessChannelMode processChannelMode; | 58 | QProcess::ProcessChannelMode processChannelMode; | ||
61 | }; | 59 | }; | ||
62 | 60 | | |||
63 | KAuth::ExecuteJob* ExternalCommand::m_job; | 61 | KAuth::ExecuteJob* ExternalCommand::m_job; | ||
64 | QCA::PrivateKey* ExternalCommand::privateKey; | | |||
65 | QCA::Initializer* ExternalCommand::init; | | |||
66 | bool ExternalCommand::helperStarted = false; | 62 | bool ExternalCommand::helperStarted = false; | ||
67 | QWidget* ExternalCommand::parent; | 63 | QWidget* ExternalCommand::parent; | ||
68 | 64 | | |||
69 | 65 | | |||
70 | /** Creates a new ExternalCommand instance without Report. | 66 | /** Creates a new ExternalCommand instance without Report. | ||
71 | @param cmd the command to run | 67 | @param cmd the command to run | ||
72 | @param args the arguments to pass to the command | 68 | @param args the arguments to pass to the command | ||
73 | */ | 69 | */ | ||
Show All 27 Lines | 93 | { | |||
101 | d->m_ExitCode = -1; | 97 | d->m_ExitCode = -1; | ||
102 | d->m_Output = QByteArray(); | 98 | d->m_Output = QByteArray(); | ||
103 | 99 | | |||
104 | d->processChannelMode = processChannelMode; | 100 | d->processChannelMode = processChannelMode; | ||
105 | } | 101 | } | ||
106 | 102 | | |||
107 | ExternalCommand::~ExternalCommand() | 103 | ExternalCommand::~ExternalCommand() | ||
108 | { | 104 | { | ||
105 | | ||||
109 | } | 106 | } | ||
110 | 107 | | |||
111 | // void ExternalCommand::setup() | 108 | /* | ||
112 | // { | 109 | void ExternalCommand::setup() | ||
113 | // connect(this, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this, &ExternalCommand::onFinished); | 110 | { | ||
114 | // connect(this, &ExternalCommand::readyReadStandardOutput, this, &ExternalCommand::onReadOutput); | 111 | connect(this, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this, &ExternalCommand::onFinished); | ||
115 | // } | 112 | connect(this, &ExternalCommand::readyReadStandardOutput, this, &ExternalCommand::onReadOutput); | ||
113 | } | ||||
114 | */ | ||||
116 | 115 | | |||
117 | /** Executes the external command. | 116 | /** Executes the external command. | ||
118 | @param timeout timeout to wait for the process to start | 117 | @param timeout timeout to wait for the process to start | ||
119 | @return true on success | 118 | @return true on success | ||
120 | */ | 119 | */ | ||
121 | bool ExternalCommand::start(int timeout) | 120 | bool ExternalCommand::start(int timeout) | ||
122 | { | 121 | { | ||
123 | Q_UNUSED(timeout) | 122 | Q_UNUSED(timeout) | ||
Show All 17 Lines | |||||
141 | } | 140 | } | ||
142 | 141 | | |||
143 | auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), | 142 | auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), | ||
144 | QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); | 143 | QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); | ||
145 | 144 | | |||
146 | interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days | 145 | interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days | ||
147 | 146 | | |||
148 | bool rval = false; | 147 | bool rval = false; | ||
149 | QByteArray request; | | |||
150 | const quint64 nonce = interface->getNonce(); | | |||
151 | request.setNum(nonce); | | |||
152 | request.append(cmd.toUtf8()); | | |||
153 | for (const auto &argument : qAsConst(d->m_Args)) | | |||
154 | request.append(argument.toUtf8()); | | |||
155 | request.append(d->m_Input); | | |||
156 | request.append(d->processChannelMode); | | |||
157 | | ||||
158 | QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); | | |||
159 | 148 | | |||
160 | QDBusPendingCall pcall = interface->start(privateKey->signMessage(hash, QCA::EMSA3_Raw), | 149 | QDBusPendingCall pcall = interface->start(cmd, args(), d->m_Input, d->processChannelMode); | ||
161 | nonce, cmd, args(), d->m_Input, d->processChannelMode); | | |||
162 | 150 | | |||
163 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); | 151 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); | ||
164 | QEventLoop loop; | 152 | QEventLoop loop; | ||
165 | 153 | | |||
166 | auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { | 154 | auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { | ||
167 | loop.exit(); | 155 | loop.exit(); | ||
168 | 156 | | |||
169 | if (watcher->isError()) | 157 | if (watcher->isError()) | ||
Show All 25 Lines | 175 | { | |||
195 | 183 | | |||
196 | // TODO KF6:Use new signal-slot syntax | 184 | // TODO KF6:Use new signal-slot syntax | ||
197 | connect(m_job, SIGNAL(percent(KJob*, unsigned long)), this, SLOT(emitProgress(KJob*, unsigned long))); | 185 | connect(m_job, SIGNAL(percent(KJob*, unsigned long)), this, SLOT(emitProgress(KJob*, unsigned long))); | ||
198 | connect(m_job, &KAuth::ExecuteJob::newData, this, &ExternalCommand::emitReport); | 186 | connect(m_job, &KAuth::ExecuteJob::newData, this, &ExternalCommand::emitReport); | ||
199 | 187 | | |||
200 | auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), | 188 | auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), | ||
201 | QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); | 189 | QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); | ||
202 | interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days | 190 | interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days | ||
203 | QByteArray request; | | |||
204 | | ||||
205 | const quint64 nonce = interface->getNonce(); | | |||
206 | request.setNum(nonce); | | |||
207 | request.append(source.path().toUtf8()); | | |||
208 | request.append(QByteArray::number(source.firstByte())); | | |||
209 | request.append(QByteArray::number(source.length())); | | |||
210 | request.append(target.path().toUtf8()); | | |||
211 | request.append(QByteArray::number(target.firstByte())); | | |||
212 | request.append(QByteArray::number(blockSize)); | | |||
213 | 191 | | |||
214 | QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); | 192 | QDBusPendingCall pcall = interface->copyblocks(source.path(), source.firstByte(), source.length(), | ||
215 | | ||||
216 | QDBusPendingCall pcall = interface->copyblocks(privateKey->signMessage(hash, QCA::EMSA3_Raw), nonce, | | |||
217 | source.path(), source.firstByte(), source.length(), | | |||
218 | target.path(), target.firstByte(), blockSize); | 193 | target.path(), target.firstByte(), blockSize); | ||
219 | 194 | | |||
220 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); | 195 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); | ||
221 | QEventLoop loop; | 196 | QEventLoop loop; | ||
222 | 197 | | |||
223 | auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { | 198 | auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { | ||
224 | loop.exit(); | 199 | loop.exit(); | ||
225 | if (watcher->isError()) | 200 | if (watcher->isError()) | ||
Show All 26 Lines | 220 | { | |||
252 | if (!QDBusConnection::systemBus().isConnected()) { | 227 | if (!QDBusConnection::systemBus().isConnected()) { | ||
253 | qWarning() << QDBusConnection::systemBus().lastError().message(); | 228 | qWarning() << QDBusConnection::systemBus().lastError().message(); | ||
254 | return false; | 229 | return false; | ||
255 | } | 230 | } | ||
256 | 231 | | |||
257 | auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), | 232 | auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), | ||
258 | QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); | 233 | QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); | ||
259 | interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days | 234 | interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days | ||
260 | QByteArray request; | | |||
261 | | ||||
262 | const quint64 nonce = interface->getNonce(); | | |||
263 | request.setNum(nonce); | | |||
264 | request.append(buffer); | | |||
265 | request.append(deviceNode.toUtf8()); | | |||
266 | request.append(QByteArray::number(firstByte)); | | |||
267 | 235 | | |||
268 | QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); | 236 | QDBusPendingCall pcall = interface->writeData(buffer, deviceNode, firstByte); | ||
269 | | ||||
270 | QDBusPendingCall pcall = interface->writeData(privateKey->signMessage(hash, QCA::EMSA3_Raw), nonce, | | |||
271 | buffer, deviceNode, firstByte); | | |||
272 | 237 | | |||
273 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); | 238 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); | ||
274 | QEventLoop loop; | 239 | QEventLoop loop; | ||
275 | 240 | | |||
276 | auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { | 241 | auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { | ||
277 | loop.exit(); | 242 | loop.exit(); | ||
278 | if (watcher->isError()) | 243 | if (watcher->isError()) | ||
279 | qWarning() << watcher->error(); | 244 | qWarning() << watcher->error(); | ||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Line(s) | 343 | { | |||
384 | QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus()); | 349 | QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus()); | ||
385 | if (iface.isValid()) { | 350 | if (iface.isValid()) { | ||
386 | exit(0); | 351 | exit(0); | ||
387 | } | 352 | } | ||
388 | 353 | | |||
389 | d->m_thread = new DBusThread; | 354 | d->m_thread = new DBusThread; | ||
390 | d->m_thread->start(); | 355 | d->m_thread->start(); | ||
391 | 356 | | |||
392 | init = new QCA::Initializer; | | |||
393 | // Generate RSA key pair for signing external command requests | | |||
394 | if (!QCA::isSupported("pkey") || !QCA::PKey::supportedIOTypes().contains(QCA::PKey::RSA)) { | | |||
395 | qCritical() << xi18n("QCA does not support RSA."); | | |||
396 | return false; | | |||
397 | } | | |||
398 | | ||||
399 | privateKey = new QCA::PrivateKey; | | |||
400 | *privateKey = QCA::KeyGenerator().createRSA(4096); | | |||
401 | if(privateKey->isNull()) { | | |||
402 | qCritical() << xi18n("Failed to make private RSA key."); | | |||
403 | return false; | | |||
404 | } | | |||
405 | | ||||
406 | if (!privateKey->canSign()) { | | |||
407 | qCritical() << xi18n("Generated key cannot be used for signatures."); | | |||
408 | return false; | | |||
409 | } | | |||
410 | | ||||
411 | QCA::PublicKey pubkey = privateKey->toPublicKey(); | | |||
412 | | ||||
413 | KAuth::Action action = KAuth::Action(QStringLiteral("org.kde.kpmcore.externalcommand.init")); | 357 | KAuth::Action action = KAuth::Action(QStringLiteral("org.kde.kpmcore.externalcommand.init")); | ||
414 | action.setHelperId(QStringLiteral("org.kde.kpmcore.externalcommand")); | 358 | action.setHelperId(QStringLiteral("org.kde.kpmcore.externalcommand")); | ||
415 | action.setTimeout(10 * 24 * 3600 * 1000); // 10 days | 359 | action.setTimeout(10 * 24 * 3600 * 1000); // 10 days | ||
416 | action.setParentWidget(parent); | 360 | action.setParentWidget(parent); | ||
417 | QVariantMap arguments; | 361 | QVariantMap arguments; | ||
418 | arguments.insert(QStringLiteral("pubkey"), pubkey.toDER()); | | |||
419 | action.setArguments(arguments); | 362 | action.setArguments(arguments); | ||
420 | m_job = action.execute(); | 363 | m_job = action.execute(); | ||
421 | m_job->start(); | 364 | m_job->start(); | ||
422 | 365 | | |||
423 | // Wait until ExternalCommand Helper is ready (helper sends newData signal just before it enters event loop) | 366 | // Wait until ExternalCommand Helper is ready (helper sends newData signal just before it enters event loop) | ||
424 | QEventLoop loop; | 367 | QEventLoop loop; | ||
425 | auto exitLoop = [&] () { loop.exit(); }; | 368 | auto exitLoop = [&] () { loop.exit(); }; | ||
426 | auto conn = QObject::connect(m_job, &KAuth::ExecuteJob::newData, exitLoop); | 369 | auto conn = QObject::connect(m_job, &KAuth::ExecuteJob::newData, exitLoop); | ||
427 | QObject::connect(m_job, &KJob::finished, [=] () { if(m_job->error()) exitLoop(); } ); | 370 | QObject::connect(m_job, &KJob::finished, [=] () { if(m_job->error()) exitLoop(); } ); | ||
428 | loop.exec(); | 371 | loop.exec(); | ||
429 | QObject::disconnect(conn); | 372 | QObject::disconnect(conn); | ||
430 | 373 | | |||
431 | helperStarted = true; | 374 | helperStarted = true; | ||
432 | return true; | 375 | return true; | ||
433 | } | 376 | } | ||
434 | 377 | | |||
435 | void ExternalCommand::stopHelper() | 378 | void ExternalCommand::stopHelper() | ||
436 | { | 379 | { | ||
437 | auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), | 380 | auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), | ||
438 | QStringLiteral("/Helper"), QDBusConnection::systemBus()); | 381 | QStringLiteral("/Helper"), QDBusConnection::systemBus()); | ||
439 | QByteArray request; | 382 | interface->exit(); | ||
440 | const quint64 nonce = interface->getNonce(); | | |||
441 | request.setNum(nonce); | | |||
442 | QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); | | |||
443 | interface->exit(privateKey->signMessage(hash, QCA::EMSA3_Raw), nonce); | | |||
444 | | ||||
445 | delete privateKey; | | |||
446 | delete init; | | |||
447 | } | | |||
448 | | ||||
449 | quint64 ExternalCommand::getNonce(QDBusInterface& iface) | | |||
450 | { | | |||
451 | QDBusPendingCall pcall = iface.asyncCall(QStringLiteral("getNonce")); | | |||
452 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall); | | |||
453 | QEventLoop loop; | | |||
454 | quint64 rval = 0; | | |||
455 | | ||||
456 | auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { | | |||
457 | loop.exit(); | | |||
458 | 383 | | |||
459 | if (watcher->isError()) | | |||
460 | qWarning() << watcher->error(); | | |||
461 | else { | | |||
462 | QDBusPendingReply<quint64> reply = *watcher; | | |||
463 | rval = reply; | | |||
464 | } | | |||
465 | }; | | |||
466 | | ||||
467 | connect(watcher, &QDBusPendingCallWatcher::finished, exitLoop); | | |||
468 | loop.exec(); | | |||
469 | return rval; | | |||
470 | } | 384 | } | ||
471 | 385 | | |||
472 | void DBusThread::run() | 386 | void DBusThread::run() | ||
473 | { | 387 | { | ||
474 | if (!QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.applicationinterface")) || | 388 | if (!QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.applicationinterface")) || | ||
475 | !QDBusConnection::systemBus().registerObject(QStringLiteral("/Application"), this, QDBusConnection::ExportAllSlots)) { | 389 | !QDBusConnection::systemBus().registerObject(QStringLiteral("/Application"), this, QDBusConnection::ExportAllSlots)) { | ||
476 | qWarning() << QDBusConnection::systemBus().lastError().message(); | 390 | qWarning() << QDBusConnection::systemBus().lastError().message(); | ||
477 | return; | 391 | return; | ||
478 | } | 392 | } | ||
479 | 393 | | |||
480 | QEventLoop loop; | 394 | QEventLoop loop; | ||
481 | loop.exec(); | 395 | loop.exec(); | ||
482 | } | 396 | } |