Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+33
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# Generates certificates from a json list of workstations and
|
||||
# generates an sql file from it
|
||||
#
|
||||
# $Id: CMakeLists.txt 47272 2019-06-21 05:31:48Z wille_m $
|
||||
# $HeadURL: svn://slssvn/TDS/branches/product/devel/trunk/Lts/Tools/Splash/CMakeLists.txt $
|
||||
# $Author: wille_m $
|
||||
#
|
||||
# Created by Mike Wille, SLTE, 2020-10-10
|
||||
#
|
||||
|
||||
if(NOT WIN32)
|
||||
if(BOTAN_FOUND AND CMAKE_VERSION VERSION_GREATER 3.15.9) # don't build AsmWorkstationCertifier if cmake < 3.16
|
||||
set(src_AsmWorkstationCertifier
|
||||
main.cpp
|
||||
)
|
||||
|
||||
NGL_ADD_EXECUTABLE(SV AsmWorkstationCertifier ${src_AsmWorkstationCertifier})
|
||||
|
||||
NGL_TARGET_LINK_LIBRARIES(AsmWorkstationCertifier
|
||||
Qt5::Core
|
||||
LtsBotan
|
||||
nglcoreapplication
|
||||
NGLLogger
|
||||
)
|
||||
|
||||
NGL_INSTALL_TARGET(AsmWorkstationCertifier)
|
||||
else()
|
||||
message(WARNING "No botan2, no AsmWorkstationCertifier")
|
||||
endif()
|
||||
else(NOT WIN32)
|
||||
message(WARNING "No Unix, no botan2, no AsmWorkstationCertifier")
|
||||
endif(NOT WIN32)
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
License for fast-ca code parts:
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Mike Wille <mike@wille.io>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
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