summaryrefslogtreecommitdiffstats
path: root/src/b2qt-update-application/update.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/b2qt-update-application/update.cpp')
-rw-r--r--src/b2qt-update-application/update.cpp202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/b2qt-update-application/update.cpp b/src/b2qt-update-application/update.cpp
new file mode 100644
index 0000000..79fea64
--- /dev/null
+++ b/src/b2qt-update-application/update.cpp
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
+
+#include "update.h"
+#include "tar.h"
+#include <QDebug>
+#include <QCoreApplication>
+#include <QProcess>
+#include <QFile>
+
+extern QStringList mounts;
+bool writeAll(const QString &fileName, const QByteArray &content);
+static QStringList allowedFiles;
+QByteArray readAll(const QString &fileName);
+bool execute(const QString &binary, const QStringList &arguments);
+bool mount(const QString &device, const QString &mountpoint, const QString &arguments = QString());
+
+QMap<QString,QString> parseMetaInfo(const QByteArray &data)
+{
+ QMap<QString,QString> rc;
+ QList<QByteArray> list = data.split('\n');
+ foreach (const QByteArray &ba, list) {
+ if (ba.isEmpty())
+ continue;
+ int i = ba.indexOf(':');
+ if (i < 0 || i == ba.size())
+ qFatal("Invalid meta.info content: No colon found or no data after colon");
+
+ QString key = ba.left(i);
+ QString value = ba.mid(i+1).trimmed();
+
+ if (rc.contains(key))
+ qFatal("Invalid meta.info content: Same key given twice");
+ rc.insert(key, value);
+ }
+ if (rc.isEmpty())
+ qFatal("Invalid meta.info content: No keys given");
+ return rc;
+}
+
+Update::Update(QObject *parent)
+ : QObject(parent)
+ , mTar(0)
+ , mSource(0)
+{
+ allowedFiles << "key.info" << "meta.info" << "keys.tar" << "uImage" << "rootfs.tar";
+}
+
+Update::~Update()
+{
+}
+
+//# openssl dgst -sha256 -sign priv.pem file.txt > file.txt.sig
+//# openssl dgst -sha256 -verify public.pem -signature file.txt.sig file.txt
+
+void Update::setDevice(QIODevice *source)
+{
+ if (!source) {
+ qFatal("Source device is NULL");
+ return;
+ }
+
+ if (mSource) {
+ qFatal("Source already set");
+ return;
+ }
+
+ mSource = source;
+ mTar = new Tar(mSource);
+
+ connect(mTar, &Tar::startingFile, this, &Update::tarStartingFile);
+ connect(mTar, &Tar::endingFile, this, &Update::tarEndingFile);
+ connect(mTar, &Tar::finished, this, &Update::tarFinished);
+}
+
+void Update::tarStartingFile(const QString &name)
+{
+ if (name == "key.info") {
+ mTar->receiveContent();
+ } else if (name.endsWith(".sig")) {
+ mTar->receiveContent();
+ mCurrentFile = name;
+ mCurrentFile.chop(4);
+
+ bool found = false;
+ while (!allowedFiles.isEmpty()) {
+ QString n = allowedFiles.takeFirst();
+ if (n == mCurrentFile) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ qFatal("File not allowed");
+
+ } else if (mCurrentFile != name) {
+ qFatal("Invalid file order");
+ } else {
+ if (mCurrentFile == "meta.info") {
+ mTar->receiveContent();
+ } else if (mCurrentFile == "uImage") {
+ mount(QString(), "/mnt/boot", "remount,rw");
+ mTar->extractContent("/mnt/boot");
+ } else if (mCurrentFile == "rootfs.tar") {
+ qDebug() << "Formatting rootfs";
+ execute("mkfs." + mMetaInfo["filesystemType"], QStringList() << mMetaInfo["rootDevice"]);
+ mount(mMetaInfo["rootDevice"], "/mnt/root");
+ mTar->extractContent("/mnt/root");
+ } else if (mCurrentFile == "keys.tar") {
+ mount(QString(), "/mnt/boot", "remount,rw");
+ execute("rm", QStringList() << "-r" << "/mnt/boot/update/keys/");
+ execute("mkdir", QStringList() << "/mnt/boot/update/keys/");
+ mTar->extractContent("/mnt/boot/update/keys");
+ } else {
+ qFatal("Unexpected file in update data");
+ }
+ }
+}
+
+void Update::tarEndingFile(const QString &name)
+{
+ if (name == "meta.info") {
+ QMap<QString,QString> map = parseMetaInfo(mTar->currentContent());
+
+ // Some rough sanity checks
+ if (!map.contains("version"))
+ qFatal("No version information found" );
+ if (map["version"] != "1")
+ qFatal("Invalid update version");
+ if (!map.contains("platform") || map["platform"].isEmpty())
+ qFatal("Platform information missing");
+ if (map["platform"] != readAll("/mnt/boot/update/platform").trimmed())
+ qFatal("Invalid platform information");
+ else
+ qDebug() << "Platform matches";
+ if (!map.contains("rootDevice") || map["rootDevice"].isEmpty())
+ qFatal("Root device information missing");
+ if (!QFile::exists(map["rootDevice"]))
+ qFatal("Root device not found");
+ if (!map.contains("key") || map["key"].isEmpty() || map["key"].contains('/'))
+ qFatal("Invalid or no key fingerprint");
+ if (!map.contains("digest") || map["digest"].isEmpty())
+ qFatal("Invalid digest type");
+ if (!map.contains("filesystemType") || map["filesystemType"].isEmpty() || map["filesystemType"].contains(' ') || map["filesystemType"].contains('/'))
+ qFatal("Invalid filesystem type");
+
+ mMetaInfo = map;
+
+ if (!mTar->setVerificationData("/mnt/boot/update/keys/" + mMetaInfo["key"], mMetaInfo["digest"]))
+ qFatal("Loading of certificate failed");
+
+ // Now verify the meta.info itself
+ if (mTar->verifyCurrentContent(mCurrentSignature)) {
+ qDebug() << "meta.info VERIFIED OK";
+ } else {
+ qDebug() << "meta.info VERIFICATION FAILED";
+ qFatal("VERIFICATION FAILED");
+ return;
+ }
+
+ } else if (name.endsWith(".sig")) {
+ mCurrentSignature = mTar->currentContent();
+ qDebug() << "Received signature with" << mCurrentSignature.size() << "bytes.";
+ } else {
+ if (mTar->checkSignature(mCurrentSignature)) {
+ qDebug() << "VERIFIED OK";
+ // FIXME: do something here
+ } else {
+ qDebug() << "VERIFICATION FAILED";
+ qFatal("VERIFICATION FAILED");
+ }
+ }
+
+ mTar->continueContent();
+}
+
+void Update::tarFinished()
+{
+ qDebug() << "Update finished";
+ mount(QString(), "/mnt/boot", "remount,rw");
+ if (!writeAll("/mnt/boot/update/state", "t"))
+ qFatal("Could not set state to testing");
+ mount(QString(), "/mnt/boot", "remount,ro");
+ qApp->exit();
+}