Skip to content
Draft
2 changes: 1 addition & 1 deletion dconfig-center/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

project(dconfig-center)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include(GNUInstallDirs)

Expand Down
51 changes: 51 additions & 0 deletions dconfig-center/dde-dconfig-daemon/dconfig_global.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
#include <QLoggingCategory>
#include "helper.hpp"

#ifdef Q_OS_LINUX
#include <cerrno>
#include <climits>
#include <cstdio>
#include <cstring>
#include <sys/types.h>
#endif

Q_DECLARE_LOGGING_CATEGORY(cfLog);

// /appid/filename/subpath/userid
Expand All @@ -26,8 +34,11 @@ using ConnServiceName = QString;
using ConnRefCount = int;
// user: u-${ConnKey}, global: g-${ResourceKey}
using ConfigCacheKey = QString;

static constexpr int TestUid = 0U;
static const QString VirtualInterAppId = "_";
const QString authorizedreadonly = QString("authorizedreadonly");
const QString authorizedreadwrite = QString("authorizedreadwrite");

inline QString formatDBusObjectPath(QString path)
{
Expand Down Expand Up @@ -231,3 +242,43 @@ inline QString configPrefixPath()
}
return path;
}

inline int check_caller_sid2(uint uid, pid_t caller_pid)
{
if (uid != 0) {
qWarning() << "[check_caller_sid2] uid != 0, uid : " << uid;
return -EPERM;
}
char sid2_attr_path[PATH_MAX] = {0};
snprintf(sid2_attr_path, PATH_MAX, "/proc/%d/attr/sid2", caller_pid);
FILE* fp_sid2_attr = fopen(sid2_attr_path, "r");
if(fp_sid2_attr == NULL){
qWarning() << QString("process %1 has no sid2\n").arg(caller_pid);
return -EPERM;
}

char sid2[256] = {0};
size_t nread = fread(sid2, 1, sizeof(sid2) - 1, fp_sid2_attr);
fclose(fp_sid2_attr);
if(nread < 1){
qWarning() << QString("process %1 %2 has no sid2\n").arg(caller_pid).arg(sid2_attr_path);
return -EPERM;
}

// [APPID_TYPE] = "deepin_app",
// [SYSID_TYPE] = "deepin_sys",
// [SECID_TYPE] = "deepin_sec",
// [安全服务] "deepin_perm_manager_sidtwo_t"
// cat /proc/[pid_t]/attr/sid2 -> system_u:object_r:deepin_sec_1_t:s0
// 只有签名的安全厂商应用运行才有此selinux2 label。
if( strstr(sid2, "deepin_app") == NULL &&
strstr(sid2, "deepin_sys") == NULL &&
strstr(sid2, "deepin_sec") == NULL &&
strstr(sid2, "perm_manager_sidtwo_t") == NULL
) {
qWarning() << QString("process %1 has [%2] no sid2 app label\n").arg(caller_pid).arg(sid2);
return -EPERM;
}

return 0;
}
73 changes: 70 additions & 3 deletions dconfig-center/dde-dconfig-daemon/dconfigconn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ void DSGConfigConn::release()
*/
void DSGConfigConn::setValue(const QString &key, const QDBusVariant &value)
{
if (!contains(key))
if (!checkValid(key))
return;

if (!hasPermissionByUid(key))
Expand All @@ -132,7 +132,7 @@ void DSGConfigConn::setValue(const QString &key, const QDBusVariant &value)

void DSGConfigConn::reset(const QString &key)
{
if (!contains(key))
if (!checkValid(key))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): reset() validates an AuthorizedReadOnly item through checkValid(), but checkValid() only verifies the SID2/root authorization and reset() never calls hasPermissionByUid() or otherwise rejects read-only permissions. An authorized root caller can therefore reset a read-only configuration item.

Triggers: When a configuration item has the newly supported AuthorizedReadOnly permission and a root caller invokes reset.

Suggested fix: Reject ReadOnly and AuthorizedReadOnly permissions in reset(), or share the same write-permission check used by setValue().

return;

qCDebug(cfLog) << "Reset value, key:" << key << ", old value:" << file()->value(key, cache());
Expand Down Expand Up @@ -230,7 +230,17 @@ QString DSGConfigConn::permissions(const QString &key)
if (!contains(key))
return QString();

return meta()->permissions(key) == DTK_CORE_NAMESPACE::DConfigFile::ReadWrite ? QString("readwrite") : QString("readonly");
switch (meta()->permissions(key)) {
case DTK_CORE_NAMESPACE::DConfigFile::ReadOnly:
return QString("readonly");
case DTK_CORE_NAMESPACE::DConfigFile::ReadWrite:
return QString("readwrite");
case DTK_CORE_NAMESPACE::DConfigFile::AuthorizedReadOnly:
return authorizedreadonly;
case DTK_CORE_NAMESPACE::DConfigFile::AuthorizedReadWrite:
return authorizedreadwrite;
}
return QString("readonly");
}

int DSGConfigConn::flags(const QString &key)
Expand All @@ -251,6 +261,45 @@ QString DSGConfigConn::getAppid() const
return QString("testappid");
}

QString DSGConfigConn::getPermissions(const QString &key)
{
switch (meta()->permissions(key)) {
case DTK_CORE_NAMESPACE::DConfigFile::ReadOnly:
return QString("readonly");
case DTK_CORE_NAMESPACE::DConfigFile::ReadWrite:
return QString("readwrite");
case DTK_CORE_NAMESPACE::DConfigFile::AuthorizedReadOnly:
return authorizedreadonly;
case DTK_CORE_NAMESPACE::DConfigFile::AuthorizedReadWrite:
return authorizedreadwrite;
}
return QString("readonly");
}

bool DSGConfigConn::checkDBusSender(const QString &key)
{
QString permission = getPermissions(key);
if (permission == authorizedreadonly || permission == authorizedreadwrite) {
if (!calledFromDBus()) {
qWarning() << "[checkDBusSender] not called by dbus.";
return false;
}
auto service = message().service();
const auto uidReply = connection().interface()->serviceUid(service);
const auto pidReply = connection().interface()->servicePid(service);
if (!uidReply.isValid() || !pidReply.isValid()) {
qWarning() << "[checkDBusSender] failed to get dbus sender uid or pid.";
return false;
}

uint uid = uidReply.value();
uint pid = pidReply.value();
qInfo() << "[checkDBusSender] dbus sender pid : " << pid;
return check_caller_sid2(uid, static_cast<pid_t>(pid)) == 0;
}
return true;
}

bool DSGConfigConn::contains(const QString &key)
{
if (containsWithoutProp(key))
Expand Down Expand Up @@ -301,3 +350,21 @@ bool DSGConfigConn::hasPermissionByUid(const QString &key) const
}
return hasPermission;
}

bool DSGConfigConn::checkValid(const QString &key)
{
bool ret = false;
if (!contains(key)) {
return ret;
}

ret = checkDBusSender(key);
if (!ret) {
QString errorMsg = QString("[%1] requires Non-sid2 configure item [%2] in [%3].").arg(getAppid()).arg(key).arg(m_key);
if (calledFromDBus())
sendErrorReply(QDBusError::Failed, errorMsg);
qWarning() << errorMsg;
}

return ret;
}
3 changes: 3 additions & 0 deletions dconfig-center/dde-dconfig-daemon/dconfigconn.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,14 @@ public Q_SLOTS: // METHODS

private:
QString getAppid() const;
QString getPermissions(const QString &key);
bool checkDBusSender(const QString &key);
bool contains(const QString &key);
DTK_CORE_NAMESPACE::DConfigMeta *meta() const;
DTK_CORE_NAMESPACE::DConfigFile *file() const;
DTK_CORE_NAMESPACE::DConfigCache *cache() const;
bool hasPermissionByUid(const QString &key) const;
bool checkValid(const QString &key);

private:
ConnKey m_key;
Expand Down
5 changes: 4 additions & 1 deletion dconfig-center/dde-dconfig-daemon/dconfigserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
#include <QLoggingCategory>
#include <QDir>
#include <QFile>
#include <DConfigFile>

#include <utility>

#include "configmanager_adaptor.h"

Expand Down Expand Up @@ -612,7 +615,7 @@ QVector<DSGConfigServer::FileSignature> DSGConfigServer::allConfigureFileSignatu
DSGConfigServer::FileSignature signature;
signature.filePath = filePath;
signature.size = fileInfo.size();
signature.changeTime = fileInfo.metadataChangeTime(QTimeZone::UTC);
signature.changeTime = fileInfo.metadataChangeTime();

signatures << signature;
}
Expand Down
1 change: 1 addition & 0 deletions dconfig-center/dde-dconfig-daemon/dconfigserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <QDBusObjectPath>
#include <QDBusContext>
#include <QDBusServiceWatcher>
#include <QDateTime>

class DSGConfigResource;
class RefManager;
Expand Down
2 changes: 1 addition & 1 deletion dconfig-center/dde-dconfig-editor/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ MainWindow::MainWindow(QWidget *parent) :
oemView->show();
});
oemView = new OEMDialog(this);
oemView->setFixedSize(QSize(800, 600));
oemView->setMinimumSize(QSize(800, 600));

installTranslate();

Expand Down
17 changes: 16 additions & 1 deletion dconfig-center/dde-dconfig-editor/oemdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
#include <DLineEdit>
#include <DSwitchButton>
#include <DSpinBox>
#include <QJsonArray>

OEMDialog::OEMDialog(QWidget *parent)
: DDialog( parent)
{
setWindowFlags(windowFlags() | Qt::WindowMinMaxButtonsHint);
m_exportView = new QTreeView();
m_exportView->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_exportView->header()->setMinimumSectionSize(400);
Expand Down Expand Up @@ -51,6 +53,8 @@ void OEMDialog::loadData(const QString &language)
m_model = new QStandardItemModel(this);
m_model->setHorizontalHeaderLabels(QStringList() << QStringLiteral("项") << QStringLiteral("值"));
m_exportView->setModel(m_model);
m_exportView->header()->setSectionResizeMode(QHeaderView::Interactive);
m_exportView->header()->resizeSection(0, 250);

const auto &apps = applications();
for (auto app : apps) {
Expand Down Expand Up @@ -249,6 +253,7 @@ void OEMDialog::displayChangedResult()
d->setAttribute(Qt::WA_DeleteOnClose, true);

QTableView *view = new QTableView(this);
view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
view->setEditTriggers(QAbstractItemView::NoEditTriggers);
view->horizontalHeader()->setStretchLastSection(true);
view->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
Expand Down Expand Up @@ -317,7 +322,17 @@ void OEMDialog::createJsonFile(const QString &fileName, const QList<DStandardIte
qWarning() << fileName;
QJsonObject rootObject, object, contentsObject, keyObject;
for (auto item : items) {
keyObject.insert("value", QJsonValue::fromVariant(item->data(ValueRole)));
QVariant variant = item->data(ValueRole);
QJsonValue val = QJsonValue::fromVariant(variant);
if (variant.canConvert<QString>()) {
QString str = variant.toString();
QJsonDocument doc = QJsonDocument::fromJson(str.toUtf8());
if (!doc.isNull() && doc.isArray()) {
val = doc.array();
}
}

keyObject.insert("value", val);
keyObject.insert("serial", 0);
keyObject.insert("permissions", "readwrite");
contentsObject.insert(item->data(KeyRole).toString(), keyObject);
Expand Down
Loading
Loading