From c273784c3efad395a913715c793feffc91f16743 Mon Sep 17 00:00:00 2001 From: Fabian Dellwing Date: Mon, 27 Mar 2023 20:04:24 +0200 Subject: [PATCH] CertificateSettings: Create basic Cert Store application This commit adds a new application named CertificateSettings that houses our Cert Store. It should be expanded in the future. --- Base/res/apps/CertificatesSettings.af | 6 ++ Base/res/icons/16x16/certificate.png | Bin 0 -> 291 bytes Base/res/icons/32x32/certificate.png | Bin 0 -> 975 bytes Userland/Applications/CMakeLists.txt | 1 + .../CertificateSettings/CMakeLists.txt | 19 ++++ .../CertificateSettings/CertificateStore.cpp | 85 ++++++++++++++++++ .../CertificateSettings/CertificateStore.gml | 40 +++++++++ .../CertificateSettings/CertificateStore.h | 52 +++++++++++ .../Applications/CertificateSettings/main.cpp | 34 +++++++ 9 files changed, 237 insertions(+) create mode 100644 Base/res/apps/CertificatesSettings.af create mode 100644 Base/res/icons/16x16/certificate.png create mode 100644 Base/res/icons/32x32/certificate.png create mode 100644 Userland/Applications/CertificateSettings/CMakeLists.txt create mode 100644 Userland/Applications/CertificateSettings/CertificateStore.cpp create mode 100644 Userland/Applications/CertificateSettings/CertificateStore.gml create mode 100644 Userland/Applications/CertificateSettings/CertificateStore.h create mode 100644 Userland/Applications/CertificateSettings/main.cpp diff --git a/Base/res/apps/CertificatesSettings.af b/Base/res/apps/CertificatesSettings.af new file mode 100644 index 0000000000..1ba704c966 --- /dev/null +++ b/Base/res/apps/CertificatesSettings.af @@ -0,0 +1,6 @@ +[App] +Name=Certificate Settings +Executable=/bin/CertificateSettings +Category=Settings +Description=Access and change the system's certificates +ExcludeFromSystemMenu=true diff --git a/Base/res/icons/16x16/certificate.png b/Base/res/icons/16x16/certificate.png new file mode 100644 index 0000000000000000000000000000000000000000..720f00d66c19da3d1501643e18ac22587d58eee6 GIT binary patch literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!cYsfbE06{PrT8VfMH{16o>{YI z&5j*AcJJPO)J0K-+yIi-|fq1?=JGccX-O9M~_~dnfdnOoDVnFe|d1|>({T} zzkd0DDChs#_WyTQUlYwg2{enTB*-tAAu>fpB?HLI_H=O!k&t9P=*ZQ?Ai{d!=db7v zF30ct6T8LII$mV6fox}B_^QNo(A}Eh!ql`+t*5K*l^6&&wB1NywQ6DY6YtesWA)eB zUCiS6o5Q|`%iNdM*4eLEKKt}UANNU9nhZIUg&GznXfUvL+4tEROP|^&aR+E1UuuMB cny)W|7Ld&WvH=J#1y2T1p00i_>zopr0C(qdC;$Ke literal 0 HcmV?d00001 diff --git a/Base/res/icons/32x32/certificate.png b/Base/res/icons/32x32/certificate.png new file mode 100644 index 0000000000000000000000000000000000000000..6346e7eaf5bcee1a5db82b680329b0b28b9df441 GIT binary patch literal 975 zcmV;=12FuFP)EX>4Tx04R}tkv&MmKpe$iQ$>*$2Rn#%$WS}kMMWG-6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|>f)s6A|?JWDYS_3;J6>}?mh0_0YaNps1~6xlS{TB$kju8X{ygP(=+EVzg?cn8?t6!oxr2_|xQ)$yEg- z#{%k5Avu2VKlt6PS(=`5lL85#|HZaHCV+umpxw0X?_=9;p8)=6;7aTI>n&jJlk{d+ ziyQ&N+rY(jSCjXE%N=0!NtX=Ck^D4;QVDoJqi-qzBe%fNn%i4vAEysMj=EaD0S*p< zXqmFtJ>EUo+uOfqI{p0s%b0S?vNv$w00006VoOIv0RI5l0A>}gwjlrj010qNS#tmY zE+YT{E+YYWr9XB6000McNliru=K~xKF*J3{b2$J202y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00E6jL_t(o!|hc~OT$1AeJR#LOYthAqOs^r2r5Dj>fcBV z^ytao;qMTF;z{t(AK;~m)j+|Eh$te(LO^LgtZ9sg&DLxPHs+E9`!2hanH@6k&CUX2 zjPc*W8IR{)lwH^6Ez9_b)Kbj9S7U z$X*YOs#Mc7#2>D6sPV206<-< zF_rV80sv5&T>!v-G3%3%zSpleO(tN5-eb + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "CertificateStore.h" +#include +#include + +namespace CertificateSettings { + +NonnullRefPtr CertificateStoreModel::create() { return adopt_ref(*new CertificateStoreModel); } + +ErrorOr CertificateStoreModel::load() +{ + // FIXME: In the future, we will allow users to import their own certificates. To support this, we would need to change this logic + auto cacert_file = TRY(Core::File::open("/etc/cacert.pem"sv, Core::File::OpenMode::Read)); + auto data = TRY(cacert_file->read_until_eof()); + m_certificates = TRY(DefaultRootCACertificates::the().reload_certificates(data)); + + return {}; +} + +DeprecatedString CertificateStoreModel::column_name(int column) const +{ + switch (column) { + case Column::IssuedTo: + return "Issued To"; + case Column::IssuedBy: + return "Issued By"; + case Column::Expire: + return "Expiration Date"; + default: + VERIFY_NOT_REACHED(); + } +} + +GUI::Variant CertificateStoreModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const +{ + if (role != GUI::ModelRole::Display) + return {}; + if (m_certificates.is_empty()) + return {}; + + auto cert = m_certificates.at(index.row()); + + switch (index.column()) { + case Column::IssuedTo: + return cert.subject.subject.is_empty() ? cert.subject.unit : cert.subject.subject; + case Column::IssuedBy: + return cert.issuer.subject.is_empty() ? cert.issuer.unit : cert.issuer.subject; + case Column::Expire: + return cert.not_after.to_deprecated_string("%Y-%m-%d"sv); + default: + VERIFY_NOT_REACHED(); + } + + return {}; +} + +ErrorOr> CertificateStoreWidget::try_create() +{ + auto widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) CertificateStoreWidget())); + TRY(widget->initialize()); + return widget; +} + +ErrorOr CertificateStoreWidget::initialize() +{ + TRY(load_from_gml(certificate_store_gml)); + + m_root_ca_tableview = find_descendant_of_type_named("root_ca_tableview"); + m_root_ca_tableview->set_highlight_selected_rows(true); + m_root_ca_tableview->set_alternating_row_colors(false); + + m_root_ca_model = CertificateStoreModel::create(); + TRY(m_root_ca_model->load()); + m_root_ca_tableview->set_model(m_root_ca_model); + m_root_ca_tableview->set_column_width(CertificateStoreModel::Column::IssuedTo, 150); + m_root_ca_tableview->set_column_width(CertificateStoreModel::Column::IssuedBy, 150); + + return {}; +} +} diff --git a/Userland/Applications/CertificateSettings/CertificateStore.gml b/Userland/Applications/CertificateSettings/CertificateStore.gml new file mode 100644 index 0000000000..4385b34109 --- /dev/null +++ b/Userland/Applications/CertificateSettings/CertificateStore.gml @@ -0,0 +1,40 @@ +@GUI::Widget { + fill_with_background_color: true + layout: @GUI::VerticalBoxLayout { + margins: [8] + } + + @GUI::GroupBox { + title: "Trusted Root Certification Authorities" + fixed_height: 500 + fixed_width: 465 + layout: @GUI::VerticalBoxLayout { + margins: [8] + } + + @GUI::TableView { + name: "root_ca_tableview" + } + + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout { + spacing: 6 + } + preferred_height: "fit" + + @GUI::Button { + name: "import_button" + text: "Import" + fixed_width: 80 + enabled: false + } + + @GUI::Button { + name: "export_button" + text: "Export" + fixed_width: 80 + enabled: false + } + } + } +} diff --git a/Userland/Applications/CertificateSettings/CertificateStore.h b/Userland/Applications/CertificateSettings/CertificateStore.h new file mode 100644 index 0000000000..ffcc3fd4f1 --- /dev/null +++ b/Userland/Applications/CertificateSettings/CertificateStore.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, Fabian Dellwing + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace CertificateSettings { + +class CertificateStoreModel : public GUI::Model { +public: + enum Column { + IssuedTo, + IssuedBy, + Expire, + __Count + }; + + static NonnullRefPtr create(); + virtual ~CertificateStoreModel() override = default; + + virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return m_certificates.size(); } + virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return Column::__Count; } + virtual DeprecatedString column_name(int) const override; + virtual GUI::Variant data(GUI::ModelIndex const&, GUI::ModelRole) const override; + virtual ErrorOr load(); + +private: + Vector m_certificates; +}; + +class CertificateStoreWidget : public GUI::SettingsWindow::Tab { + C_OBJECT_ABSTRACT(CertStoreWidget) +public: + virtual ~CertificateStoreWidget() override = default; + static ErrorOr> try_create(); + virtual void apply_settings() override {}; + +private: + CertificateStoreWidget() = default; + ErrorOr initialize(); + + RefPtr m_root_ca_model; + RefPtr m_root_ca_tableview; +}; +} diff --git a/Userland/Applications/CertificateSettings/main.cpp b/Userland/Applications/CertificateSettings/main.cpp new file mode 100644 index 0000000000..e434f6c88c --- /dev/null +++ b/Userland/Applications/CertificateSettings/main.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023, Fabian Dellwing + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "CertificateStore.h" + +#include +#include +#include +#include +#include +#include + +ErrorOr serenity_main(Main::Arguments args) +{ + TRY(Core::System::pledge("stdio rpath wpath cpath recvfd sendfd unix")); + + auto app = TRY(GUI::Application::try_create(args)); + + TRY(Core::System::unveil("/res", "r")); + TRY(Core::System::unveil("/etc/cacert.pem", "r")); + TRY(Core::System::unveil("/etc/timezone", "r")); + TRY(Core::System::unveil(nullptr, nullptr)); + + auto app_icon = GUI::Icon::default_icon("certificate"sv); + auto window = TRY(GUI::SettingsWindow::create("Certificates", GUI::SettingsWindow::ShowDefaultsButton::No)); + auto cert_store_widget = TRY(window->add_tab(TRY("Certificate Store"_string), "certificate"sv)); + window->set_icon(app_icon.bitmap_for_size(16)); + + window->show(); + return app->exec(); +}