KItinerary

cert-downloader.cpp
1/*
2 SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "vdvcertificate_p.h"
8
9#include <QCoreApplication>
10#include <QDate>
11#include <QDebug>
12#include <QFile>
13#include <QProcess>
14#include <QRegularExpression>
15
16#include <vector>
17
18using namespace KItinerary;
19
20static std::vector<QString> listCerts()
21{
22 QProcess proc;
23 proc.setProgram(QStringLiteral("kioclient"));
24 proc.setArguments({QStringLiteral("ls"), QStringLiteral("ldap://ldap-vdv-ion.telesec.de:389/ou=VDV%20KA,o=VDV%20Kernapplikations%20GmbH,c=de")});
26 proc.start();
27 if (!proc.waitForFinished() || proc.exitStatus() != QProcess::NormalExit) {
28 qFatal("Failed to list certificates from LDAP server.");
29 }
30
31 std::vector<QString> certs;
32 for (const auto &line : proc.readAllStandardOutput().split('\n')) {
33 if (line.size() <= 5) {
34 continue;
35 }
36 certs.push_back(QString::fromUtf8(line.left(line.size() - 5)));
37 }
38 return certs;
39}
40
41static void downloadCert(const QString &certName)
42{
43 QProcess proc;
44 proc.setProgram(QStringLiteral("kioclient"));
45 proc.setArguments({QStringLiteral("cat"), QStringLiteral("ldap://ldap-vdv-ion.telesec.de:389/cn=") + certName + QStringLiteral(",ou=VDV%20KA,o=VDV%20Kernapplikations%20GmbH,c=de")});
47 proc.start();
48 if (!proc.waitForFinished() || proc.exitStatus() != QProcess::NormalExit) {
49 qFatal("Failed to download certificate %s from LDAP server.", qPrintable(certName));
50 }
51
52 // primitive LDIF parser, would be nicer with something like KLDAP
53 const auto certLdif = QString::fromUtf8(proc.readAllStandardOutput());
54 QRegularExpression regExp(QStringLiteral("cACertificate:: ([\\w\\W]*?)\n[^ ]"));
55 const auto match = regExp.match(certLdif);
56 const auto certData = match.captured(1).remove(QLatin1Char('\n')).remove(QLatin1Char(' ')).toUtf8();
57
58 QFile f(certName + QLatin1StringView(".vdv-cert"));
59 f.open(QFile::WriteOnly);
60 f.write(QByteArray::fromBase64(certData));
61}
62
63static void writeQrc(const std::vector<QString> &certNames)
64{
65 QFile qrc(QStringLiteral("vdv-certs.qrc"));
66 if (!qrc.open(QFile::WriteOnly)) {
67 qFatal("Failed to open file %s: %s", qPrintable(qrc.fileName()), qPrintable(qrc.errorString()));
68 }
69 qrc.write(R"(<!--
70 SPDX-FileCopyrightText: none
71 SPDX-License-Identifier: CC0-1.0
72-->
73<RCC>
74 <qresource prefix="/org.kde.pim/kitinerary/vdv/certs">
75)");
76 for (const auto &certName : certNames) {
77 qrc.write(" <file>");
78 qrc.write(certName.toUtf8());
79 qrc.write(".vdv-cert</file>\n");
80 }
81 qrc.write(" </qresource>\n</RCC>\n");
82}
83
84static VdvCertificate loadCert(const QString &certName)
85{
86 QFile f(certName + QLatin1StringView(".vdv-cert"));
87 if (!f.open(QFile::ReadOnly)) {
88 qFatal("Failed to open file %s: %s", qPrintable(f.fileName()),
89 qPrintable(f.errorString()));
90 }
91 return VdvCertificate(f.readAll());
92}
93
94static void decodeCert(const QString &certName)
95{
96 auto cert = loadCert(certName);
97 if (cert.needsCaKey()) {
98 qDebug() << certName << "needs decoding";
99 const auto rootCa = loadCert(QStringLiteral("4555564456100106"));
100 cert.setCaCertificate(rootCa);
101 if (cert.isValid()) {
102 QFile f(certName + QLatin1StringView(".vdv-cert"));
103 if (!f.open(QFile::WriteOnly)) {
104 qFatal("Failed to open file %s: %s", qPrintable(f.fileName()),
105 qPrintable(f.errorString()));
106 }
107 cert.writeKey(&f);
108 } else {
109 qFatal("Decoding failed for %s", qPrintable(certName));;
110 }
111 } else if (cert.isValid()) {
112 // this removes the signature and other unknown elements, leaving just the key
113 QFile f(certName + QLatin1StringView(".vdv-cert"));
114 if (!f.open(QFile::WriteOnly)) {
115 qFatal("Failed to open file %s: %s", qPrintable(f.fileName()), qPrintable(f.errorString()));
116 }
117 cert.writeKey(&f);
118 } else {
119 qWarning("%s is invalid", qPrintable(certName));
120 }
121}
122
123int main(int argc, char **argv)
124{
125 QCoreApplication app(argc, argv);
126
127 // (1) list all certificates
128 auto certNames = listCerts();
129
130 // (2) load all certificates we don't have yet
131 for (auto it = certNames.begin(); it != certNames.end();) {
132 if (QFile::exists(QLatin1Char('.') + (*it) +
133 QLatin1StringView(".vdv-cert"))) {
134 // expired certificate, but cached from previous run
135 it = certNames.erase(it);
136 continue;
137 }
138 qDebug() << "checking certificate" << (*it);
139 if (!QFile::exists((*it) + QLatin1StringView(".vdv-cert"))) {
140 downloadCert(*it);
141 }
142 ++it;
143 }
144
145 // (3) decode certificates (avoids runtime cost and shrinks the file size)
146 for (const auto &certName : certNames) {
147 decodeCert(certName);
148 }
149
150 // (4) discard old sub-CA certificates we don't need
151 for (auto it = certNames.begin(); it != certNames.end();) {
152 const auto cert = loadCert(*it);
153 if (!cert.isValid()) {
154 qWarning("Invalid certificate: %s", qPrintable(*it));
155 it = certNames.erase(it);
156 continue;
157 }
158 if (!cert.isSelfSigned() && cert.endOfValidity().year() < 2019) {
159 qDebug() << "discarding" << (*it) << "due to being expired" << cert.endOfValidity();
160 QFile::rename((*it) + QLatin1StringView(".vdv-cert"),
161 QLatin1Char('.') + (*it) +
162 QLatin1String(".vdv-cert"));
163 it = certNames.erase(it);
164 continue;
165 }
166 ++it;
167 }
168
169 // (5) write qrc file
170 std::sort(certNames.begin(), certNames.end());
171 writeQrc(certNames);
172
173 return 0;
174}
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
Classes for reservation/travel data models, data extraction and data augmentation.
Definition berelement.h:17
QByteArray fromBase64(const QByteArray &base64, Base64Options options)
bool exists() const const
bool rename(const QString &newName)
ForwardedErrorChannel
QProcess::ExitStatus exitStatus() const const
QByteArray readAllStandardOutput()
void setArguments(const QStringList &arguments)
void setProcessChannelMode(ProcessChannelMode mode)
void setProgram(const QString &program)
void start(OpenMode mode)
bool waitForFinished(int msecs)
QString fromUtf8(QByteArrayView str)
QByteArray toUtf8() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:49 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.