Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
Executable
+264
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Generates an SQL file from a workstation JSON file (usually located under Lts/Tests/DatabaseInitialisation/%project%_asm_values_workstations.json)
|
||||
* $Id: tpm_exported_scripts_dialog.cpp 52234 2020-10-01 13:34:21Z wille_m $
|
||||
* $HeadURL: svn://slssvn/TDS/branches/product/devel/trunk/Lts/Plugins/TakeHomePackageManager/tpm_exported_scripts_dialog.cpp $
|
||||
* $Author: wille_m $
|
||||
*
|
||||
* Created by Mike Wille, SLTE, 2020-10-10
|
||||
*/
|
||||
|
||||
// Parts stolen from https://github.com/wille-io/fast-ca/blob/master/main.cpp @ https://github.com/wille-io/fast-ca/blob/7cd703a2751d995da5b0f918198532d4955934f6/main.cpp
|
||||
|
||||
#include <botan/x509_ca.h>
|
||||
#include <botan/x509cert.h>
|
||||
#include <botan/x509self.h>
|
||||
#include <botan/rsa.h>
|
||||
#include <botan/pk_keys.h>
|
||||
#include <botan/bigint.h>
|
||||
#include <botan/auto_rng.h>
|
||||
#include <botan/pubkey.h> // bug: botan 2.12.1 does a sizeof for (undefined) PK_Signer when using X509CA
|
||||
#include <botan/pkcs8.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unistd.h> // grr.. not portable
|
||||
#include <termios.h> // blank pwd / echo on/off
|
||||
#include <iostream>
|
||||
|
||||
#include <nglapplication/nglcoreapplication.h>
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QSaveFile>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QTextStream>
|
||||
|
||||
|
||||
using namespace Botan;
|
||||
termios t;
|
||||
|
||||
|
||||
void echoRestore()
|
||||
{
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &t);
|
||||
}
|
||||
|
||||
|
||||
void echoOff()
|
||||
{
|
||||
termios _t;
|
||||
tcgetattr(STDIN_FILENO, &_t);
|
||||
t = _t;
|
||||
|
||||
_t.c_lflag &= ~ECHO;
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &_t);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
NGLCoreApplication app(argc, argv);
|
||||
app.createLogger("AsmWorkstationCertifier");
|
||||
|
||||
NGL::INGLLogger &logger(app.baseLogger());
|
||||
STARTUP_LOG(logger);
|
||||
|
||||
QStringList args(app.arguments());
|
||||
|
||||
if (args.count() < 5)
|
||||
{
|
||||
ERROR_LOG(logger) << "usage: <input (project_asm_values_worksations.json)>"
|
||||
<< " <output (project_asm_values_workstations.sql)>"
|
||||
<< " <ca certificate (ca.crt)>"
|
||||
<< " <ca private key (ca.ber)>";
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString inputFile(args.at(1));
|
||||
QString outputFile(args.at(2));
|
||||
|
||||
DEBUG_LOG(logger) << "input file: " << inputFile << ", output file: " << outputFile;
|
||||
|
||||
QFile input(inputFile);
|
||||
|
||||
if (!input.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
{
|
||||
ERROR_LOG(logger) << "cannot open input file: " << input.errorString();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// botan stuff
|
||||
INFO_LOG(logger) << "Loading CA certificate ...";
|
||||
X509_Certificate caCert = X509_Certificate(args.at(3).toStdString());
|
||||
AutoSeeded_RNG rng;
|
||||
|
||||
|
||||
INFO_LOG(logger) << "Loading CA key ...";
|
||||
Private_Key *caPrivKey = nullptr;
|
||||
|
||||
std::string pwd;
|
||||
printf("> Enter the CA's private key's password: ");
|
||||
|
||||
echoOff();
|
||||
std::cin >> pwd;
|
||||
echoRestore();
|
||||
printf("\n");
|
||||
|
||||
try
|
||||
{
|
||||
caPrivKey = PKCS8::load_key(args.at(4).toStdString(), rng, pwd);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
ERROR_LOG(logger) << "CA keys couldn't be unlocked with the given password!";
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
INFO_LOG(logger) << "Initializing CA ...";
|
||||
X509_CA ca = X509_CA(caCert, *caPrivKey, "SHA-256", rng);
|
||||
// TODO: free caPrivKey
|
||||
|
||||
|
||||
|
||||
QSaveFile output(outputFile);
|
||||
|
||||
if (!output.open(QIODevice::WriteOnly | QIODevice::Truncate /* empty file */ | QIODevice::Text))
|
||||
{
|
||||
ERROR_LOG(logger) << "cannot open / create output file: " << output.errorString();
|
||||
return -1;
|
||||
}
|
||||
|
||||
QTextStream os(&output);
|
||||
|
||||
// INSERT INTO asm.asm_asset(id, name, asset_type_id, location_id, certificate) VALUES
|
||||
|
||||
os << "-- Do not edit this file! File was generated with: " << args.join(QStringLiteral(" ")) << endl
|
||||
<< endl
|
||||
<< endl
|
||||
<< "-- @RDE_HEADER@" << endl
|
||||
<< "-- SQL file for asm.asm_asset with generated certificates per hostname" << endl
|
||||
<< "-- $Id: $" << endl
|
||||
<< "-- $HeadURL: $" << endl
|
||||
<< "-- $Author: $" << endl
|
||||
<< endl
|
||||
<< "-- Created by Mike Wille, SLTE, 2020-10-10" << endl
|
||||
<< endl
|
||||
<< endl;
|
||||
|
||||
|
||||
os << "-- Workstations";
|
||||
|
||||
|
||||
QJsonParseError err;
|
||||
QJsonDocument doc(QJsonDocument::fromJson(input.readAll(), &err));
|
||||
|
||||
if (err.error != QJsonParseError::NoError)
|
||||
{
|
||||
ERROR_LOG(logger) << "JSON input file " << inputFile << " contains an invalid json document: " << err.errorString() << " at offset " << err.offset;
|
||||
return -1;
|
||||
}
|
||||
|
||||
QJsonObject root(doc.object());
|
||||
|
||||
if (root.isEmpty())
|
||||
{
|
||||
ERROR_LOG(logger) << "JSON input file " << inputFile << " is missing the root object";
|
||||
return -1;
|
||||
}
|
||||
|
||||
QJsonArray grpArr(root[QStringLiteral("groups")].toArray());
|
||||
|
||||
if (grpArr.isEmpty())
|
||||
{
|
||||
ERROR_LOG(logger) << "JSON input file " << inputFile << " is missing the groups array";
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (QJsonValueRef grpRef : grpArr)
|
||||
{
|
||||
const QJsonObject grp(grpRef.toObject());
|
||||
|
||||
os << endl << endl << "-- " << grp[QStringLiteral("name")].toString() << endl;
|
||||
os << "INSERT INTO asm.asm_asset(id, name, asset_type_id, location_id, certificate) VALUES" << endl;
|
||||
|
||||
QJsonArray wsArr(grp[QStringLiteral("workstations")].toArray());
|
||||
|
||||
if (wsArr.isEmpty())
|
||||
{
|
||||
ERROR_LOG(logger) << "JSON input file " << inputFile << " is missing the workstations array in a group";
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
for (const QJsonValue &wsRef : qAsConst(wsArr))
|
||||
{
|
||||
const QJsonObject ws(wsRef.toObject());
|
||||
|
||||
int id = ws[QStringLiteral("id")].toInt();
|
||||
QString host = ws[QStringLiteral("host")].toString();
|
||||
int asset = ws[QStringLiteral("asset")].toInt();
|
||||
int location = ws[QStringLiteral("location")].toInt();
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
os << endl << ",";
|
||||
|
||||
|
||||
INFO_LOG(logger) << "Creating client keys for " << host << "...";
|
||||
RSA_PrivateKey privKey = RSA_PrivateKey(rng, 4096);
|
||||
std::string fqdn(host.toStdString());
|
||||
|
||||
|
||||
INFO_LOG(logger) << "Creating client certificate request ...";
|
||||
X509_Cert_Options opts("");
|
||||
opts.common_name = fqdn;
|
||||
opts.dns = fqdn;
|
||||
opts.add_constraints(Key_Constraints::DIGITAL_SIGNATURE);
|
||||
opts.add_ex_constraint("PKIX.ClientAuth");
|
||||
PKCS10_Request request = X509::create_cert_req(opts, privKey, "SHA-256", rng);
|
||||
|
||||
|
||||
INFO_LOG(logger) << "Creating client certificate ...";
|
||||
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
||||
X509_Certificate cert = ca.sign_request(request, rng, X509_Time(now),
|
||||
X509_Time(now + std::chrono::hours(20 * 365 * 24))); // creates a certificate that is valid from now, for incredible 20 years (that should really be enough for a software in an age of super fast code depreciation)
|
||||
std::string certData = cert.PEM_encode();
|
||||
|
||||
|
||||
os << "( " << id << ", '" << host << "', " << asset << ", " << location << ", decode('"
|
||||
<< QString::fromUtf8(QString::fromStdString(certData).toUtf8().toHex()) // wow
|
||||
<< "', 'hex'))";
|
||||
|
||||
DEBUG_LOG(logger) << "Worksation '"<<ws[QStringLiteral("host")].toString()<<"' done!";
|
||||
}
|
||||
|
||||
os << ";";
|
||||
DEBUG_LOG(logger) << "Group '"<<grp[QStringLiteral("name")].toString()<<"' done!";
|
||||
}
|
||||
|
||||
os << endl
|
||||
<< endl
|
||||
<< "ALTER SEQUENCE asm.asm_asset_id_seq RESTART WITH 200;";
|
||||
|
||||
os.flush();
|
||||
|
||||
if (!output.commit())
|
||||
{
|
||||
ERROR_LOG(logger) << "Unable to commit output file data! " << output.errorString();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
SHUTDOWN_LOG(logger);
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user