pki: use libtls for pki --est|--estca

This commit is contained in:
Andreas Steffen
2022-08-25 10:50:40 +02:00
parent 976c74b772
commit 784606a827
10 changed files with 183 additions and 236 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ pki_SOURCES = pki.c pki.h pki_cert.c pki_cert.h command.c command.h \
commands/self.c \
commands/signcrl.c \
commands/verify.c \
est/est.h est/est.c est/est_tls.h est/est_tls.c \
est/est_tls.h est/est_tls.c \
scep/scep.h scep/scep.c
pki_LDADD = \
+6 -7
View File
@@ -19,7 +19,6 @@
#include "pki.h"
#include "pki_cert.h"
#include "est/est.h"
#include "est/est_tls.h"
#include <credentials/certificates/certificate.h>
@@ -64,7 +63,7 @@ static int est()
case 'i': /* --in */
file = arg;
continue;
case 'c': /* --cacert */
case 'C': /* --cacert */
cacert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
CERT_X509, BUILD_FROM_FILE, arg, BUILD_END);
if (!cacert)
@@ -74,7 +73,7 @@ static int est()
}
creds->add_cert(creds, TRUE, cacert);
continue;
case 'o': /* --cert */
case 'c': /* --cert */
client_cert_file = arg;
continue;
case 'X': /* --certid */
@@ -193,7 +192,7 @@ static int est()
{
chunk_t handle;
if (client_cert_file) /* loadold certificate file */
if (client_cert_file) /* load old certificate file */
{
client_cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
CERT_X509,
@@ -362,9 +361,9 @@ static void __attribute__ ((constructor))reg()
{"help", 'h', 0, "show usage information"},
{"url", 'u', 1, "URL of the EST server"},
{"in", 'i', 1, "PKCS#10 input file, default: stdin"},
{"cacert", 'c', 1, "CA certificate"},
{"cert", 'o', 1, "old certificate about to be renewed"},
{"certid", 'X', 1, "smartcard or TPM certificate object handle" },
{"cacert", 'C', 1, "CA certificate"},
{"cert", 'c', 1, "old certificate about to be renewed"},
{"certid", 'X', 1, "smartcard or TPM certificate object handle" },
{"key", 'k', 1, "old private key about to be replaced"},
{"keyid", 'x', 1, "smartcard or TPM private key object handle"},
{"userpass", 'p', 1, "username:password for http basic auth"},
+60 -18
View File
@@ -16,7 +16,7 @@
#include "pki.h"
#include "pki_cert.h"
#include "est/est.h"
#include "est/est_tls.h"
#include <credentials/containers/pkcs7.h>
#include <credentials/certificates/certificate.h>
@@ -29,35 +29,55 @@ static int estca()
{
cred_encoding_type_t form = CERT_ASN1_DER;
chunk_t est_response = chunk_empty;
char *arg, *url = NULL, *caout = NULL;
certificate_t *cacert;
mem_cred_t *creds = NULL;
est_tls_t *est_tls;
char *arg, *error = NULL, *url = NULL, *caout = NULL;
bool force = FALSE, success;
u_int http_code = 0;
status_t status = 1;
/* initialize CA certificate storage */
creds = mem_cred_create();
lib->credmgr->add_set(lib->credmgr, &creds->set);
while (TRUE)
{
switch (command_getopt(&arg))
{
case 'h':
return command_usage(NULL);
case 'u':
case 'h': /* --help */
goto usage;
case 'u': /* --url */
url = arg;
continue;
case 'c':
case 'C': /* --cacert */
cacert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
CERT_X509, BUILD_FROM_FILE, arg, BUILD_END);
if (!cacert)
{
DBG1(DBG_APP, "could not load cacert file '%s'", arg);
goto err;
}
creds->add_cert(creds, TRUE, cacert);
continue;
case 'c': /* --caout */
caout = arg;
continue;
case 'f':
case 'f': /* --outform */
if (!get_form(arg, &form, CRED_CERTIFICATE))
{
return command_usage("invalid certificate output format");
error ="invalid certificate output format";
goto usage;
}
continue;
case 'F':
case 'F': /* --force */
force = TRUE;
continue;
case EOF:
break;
default:
return command_usage("invalid --estca option");
error ="invalid --estca option";
goto usage;
}
break;
}
@@ -67,17 +87,38 @@ static int estca()
return command_usage("--url is required");
}
if (!est_https_request(url, EST_CACERTS, FALSE, chunk_empty, &est_response,
&http_code))
est_tls = est_tls_create(url, NULL, NULL);
if (!est_tls)
{
DBG1(DBG_APP, "did not receive a valid EST response: HTTP %u", http_code);
return 1;
DBG1(DBG_APP, "TLS connection to EST server was not established");
goto err;
}
success = pki_cert_extract_cacerts(est_response, caout, NULL, TRUE, form,
force);
success = est_tls->request(est_tls, EST_CACERTS, chunk_empty, &est_response,
&http_code, NULL);
est_tls->destroy(est_tls);
if (!success)
{
DBG1(DBG_APP, "EST request failed: HTTP %u", http_code);
goto err;
}
if (pki_cert_extract_cacerts(est_response, caout, NULL, TRUE, form, force))
{
status = 0;
}
err:
lib->credmgr->remove_set(lib->credmgr, &creds->set);
creds->destroy(creds);
chunk_free(&est_response);
return success ? 0 : 1;
return status;
usage:
lib->credmgr->remove_set(lib->credmgr, &creds->set);
creds->destroy(creds);
return command_usage(error);
}
/**
@@ -88,10 +129,11 @@ static void __attribute__ ((constructor))reg()
command_register((command_t) {
estca, 'e', "estca",
"get CA certificate[s] from a EST server",
{"--url url [--caout file] [--outform der|pem] [--force]"},
{"--url url [--cacert file]+ [--caout file] [--outform der|pem] [--force]"},
{
{"help", 'h', 0, "show usage information"},
{"url", 'u', 1, "URL of the SCEP server"},
{"cacert", 'C', 1, "TLS CA certificate"},
{"caout", 'c', 1, "CA certificate [template]"},
{"outform", 'f', 1, "encoding of stored certificates, default: der"},
{"force", 'F', 0, "force overwrite of existing files"},
+32 -30
View File
@@ -39,7 +39,7 @@ static int scep()
{
char *arg, *url = NULL, *file = NULL, *dn = NULL, *error = NULL;
char *ca_enc_file = NULL, *ca_sig_file = NULL;
char *old_cert_file = NULL, *old_key_file = NULL;
char *client_cert_file = NULL, *client_key_file = NULL;
cred_encoding_type_t form = CERT_ASN1_DER;
chunk_t scep_response = chunk_empty;
chunk_t challenge_password = chunk_empty;
@@ -95,33 +95,33 @@ static int scep()
{
switch (command_getopt(&arg))
{
case 'h':
case 'h': /* --help */
goto usage;
case 'u':
case 'u': /* --url */
url = arg;
continue;
case 'i':
case 'i': /* --in */
file = arg;
continue;
case 'd':
case 'd': /* --dn */
dn = arg;
continue;
case 'a':
case 'a': /* --san */
san->insert_last(san, identification_create_from_string(arg));
continue;
case 'P':
case 'P': /* --profile */
cert_type = chunk_create(arg, strlen(arg));
continue;
case 'p':
case 'p': /* --password */
challenge_password = chunk_create(arg, strlen(arg));
continue;
case 'e':
case 'e': /* --cacert-enc */
ca_enc_file = arg;
continue;
case 's':
case 's': /* --cacert-sig */
ca_sig_file = arg;
continue;
case 'c':
case 'C': /* --cacert */
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_FROM_FILE, arg, BUILD_END);
if (!cert)
@@ -131,13 +131,13 @@ static int scep()
}
creds->add_cert(creds, TRUE, cert);
continue;
case 'o':
old_cert_file = arg;
case 'c': /* --cert */
client_cert_file = arg;
continue;
case 'k':
old_key_file = arg;
case 'k': /* --key */
client_key_file = arg;
continue;
case 'C':
case 'E': /* --cipher */
if (strcaseeq(arg, "des3"))
{
cipher = ENCR_3DES;
@@ -154,14 +154,14 @@ static int scep()
goto usage;
}
continue;
case 'g':
case 'g': /* --digest */
if (!enum_from_name(hash_algorithm_short_names, arg, &digest_alg))
{
error = "invalid --digest type";
goto usage;
}
continue;
case 'R':
case 'R': /* --rsa-padding */
if (streq(arg, "pss"))
{
pss = TRUE;
@@ -186,7 +186,7 @@ static int scep()
case 'm': /* --maxpolltime */
max_poll_time = atoi(optarg);
continue;
case 'f':
case 'f': /* --form */
if (!get_form(arg, &form, CRED_CERTIFICATE))
{
error = "invalid certificate output format";
@@ -220,7 +220,7 @@ static int scep()
goto usage;
}
if (old_cert_file && !old_key_file)
if (client_cert_file && !client_key_file)
{
error = "--oldkey is required if --oldcert is set";
goto usage;
@@ -374,7 +374,7 @@ static int scep()
}
DBG1(DBG_APP, "transaction ID: %.*s", (int)transID.len, transID.ptr);
if (old_cert_file)
if (client_cert_file)
{
/* check support of Renewal Operation */
if (!(caps_flags & SCEP_CAPS_RENEWAL))
@@ -390,19 +390,21 @@ static int scep()
/* load old client certificate */
x509_signer = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_FROM_FILE, old_cert_file, BUILD_END);
BUILD_FROM_FILE, client_cert_file, BUILD_END);
if (!x509_signer)
{
DBG1(DBG_APP, "could not load old cert file '%s'", old_cert_file);
DBG1(DBG_APP, "loading client cert file '%s' failed",
client_cert_file);
goto err;
}
/* load old RSA private key */
priv_signer = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA,
BUILD_FROM_FILE, old_key_file, BUILD_END);
BUILD_FROM_FILE, client_key_file, BUILD_END);
if (!priv_signer)
{
DBG1(DBG_APP, "parsing old private key failed");
DBG1(DBG_APP, "loading client private key file '%s' failed",
client_key_file);
goto err;
}
}
@@ -456,7 +458,7 @@ static int scep()
DBG1(DBG_APP, "could not load signature cacert file '%s'", ca_sig_file);
goto end;
}
creds->add_cert(creds, TRUE, x509_ca_sig->get_ref(x509_ca_sig));
x509_ca_sig = creds->add_cert_ref(creds, TRUE, x509_ca_sig);
/* build pkcs7 request */
pkcs7_req = scep_build_request(pkcs10_encoding, transID, scep_msg_type,
@@ -642,10 +644,10 @@ static void __attribute__ ((constructor))reg()
{"password", 'p', 1, "challengePassword to include in cert request"},
{"cacert-enc", 'e', 1, "CA certificate for encryption"},
{"cacert-sig", 's', 1, "CA certificate for signature verification"},
{"cacert", 'c', 1, "Additional CA certificates"},
{"oldcert", 'o', 1, "Old certificate about to be renewed"},
{"oldkey", 'k', 1, "Old RSA private key about to be replaced"},
{"cipher", 'C', 1, "encryption cipher, default: aes"},
{"cacert", 'C', 1, "Additional CA certificates"},
{"cert", 'c', 1, "Old certificate about to be renewed"},
{"key", 'k', 1, "Old RSA private key about to be replaced"},
{"cipher", 'E', 1, "encryption cipher, default: aes"},
{"digest", 'g', 1, "digest for signature creation, default: sha256"},
{"rsa-padding", 'R', 1, "padding for RSA signatures, default: pkcs1"},
{"interval", 't', 1, "poll interval, default: 60s"},
+6 -6
View File
@@ -37,24 +37,24 @@ static int scepca()
{
switch (command_getopt(&arg))
{
case 'h':
case 'h': /* --help */
return command_usage(NULL);
case 'u':
case 'u': /* --url */
url = arg;
continue;
case 'c':
case 'c': /* --caout */
caout = arg;
continue;
case 'r':
case 'r': /* --raout */
raout = arg;
continue;
case 'f':
case 'f': /* --form */
if (!get_form(arg, &form, CRED_CERTIFICATE))
{
return command_usage("invalid certificate output format");
}
continue;
case 'F':
case 'F': /* --force */
force = TRUE;
continue;
case EOF:
-112
View File
@@ -1,112 +0,0 @@
/*
* Copyright (C) 2022 Andreas Steffen, strongSec GmbH
*
* Copyright (C) secunet Security Networks AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include "est.h"
#define HTTP_CODE_OK 200
static const char *operations[] = {
"cacerts",
"simpleenroll",
"simplereenroll",
"fullcmc",
"serverkeygen",
"csrattrs"
};
static const char *request_types[] = {
"",
"application/pkcs10",
"application/pkcs10",
"application/pkcs7-mime",
"application/pkcs10",
""
};
/**
* Send an EST request via HTTPS and wait for a response
*/
bool est_https_request(const char *url, est_op_t op, bool http_post,
chunk_t data, chunk_t *response, u_int *http_code)
{
host_t *srcip = NULL;
char *complete_url = NULL;
status_t status;
uint32_t http_timeout = lib->settings->get_time(lib->settings,
"%s.est.http_timeout", 30, lib->ns);
char *http_bind = lib->settings->get_str(lib->settings,
"%s.est.http_bind", NULL, lib->ns);
/* initialize response */
*response = chunk_empty;
*http_code = 0;
/* construct complete EST URL */
if (asprintf(&complete_url, "%s/.well-known/est/%s", url, operations[op]) == -1)
{
DBG1(DBG_APP, "could not allocate complete_url string");
return FALSE;
}
DBG2(DBG_APP, "sending EST request to '%s'", url);
if (http_bind)
{
srcip = host_create_from_string(http_bind, 0);
}
if (http_post)
{
status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
FETCH_TIMEOUT, http_timeout,
FETCH_REQUEST_DATA, data,
FETCH_REQUEST_TYPE, request_types[op],
FETCH_REQUEST_HEADER, "Expect:",
FETCH_SOURCEIP, srcip,
FETCH_RESPONSE_CODE, http_code,
FETCH_END);
}
else /* HTTP_GET */
{
status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
FETCH_TIMEOUT, http_timeout,
FETCH_SOURCEIP, srcip,
FETCH_RESPONSE_CODE, http_code,
FETCH_END);
}
DESTROY_IF(srcip);
free(complete_url);
if (status != SUCCESS)
{
return FALSE;
}
if (*http_code == HTTP_CODE_OK)
{
chunk_t base64_response = *response;
*response = chunk_from_base64(base64_response, NULL);
chunk_free(&base64_response);
}
return TRUE;
}
-40
View File
@@ -1,40 +0,0 @@
/*
* Copyright (C) 2022 Andreas Steffen, strongSec GmbH
*
* Copyright (C) secunet Security Networks AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#ifndef _EST_H
#define _EST_H
#include <library.h>
/**
* EST (RFC 7030) Operations
*/
typedef enum {
EST_CACERTS,
EST_SIMPLE_ENROLL,
EST_SIMPLE_REENROLL,
EST_FULL_CMC,
EST_SERVER_KEYGEN,
EST_CSR_ATTRS
} est_op_t;
/**
* Send an EST request via HTTPS and wait for a response
*/
bool est_https_request(const char *url, est_op_t op, bool http_post,
chunk_t data, chunk_t *response, u_int *http_code);
#endif /* _EST_H */
+12 -4
View File
@@ -16,8 +16,6 @@
#define _GNU_SOURCE /* for asprintf() */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
@@ -133,7 +131,8 @@ static chunk_t build_http_request(private_est_tls_t *this, est_op_t op, chunk_t
len = asprintf(&http_header,
"GET %s/.well-known/est/%s HTTP/1.1\r\n"
"Host: %s\r\n"
"%s",
"%s"
"\r\n",
this->http_path, operations[op], this->http_host, http_auth);
if (len > 0)
{
@@ -154,6 +153,11 @@ static bool parse_http_header(chunk_t *in, u_int *http_code, u_int *content_len
*content_len = 0;
*base64 = FALSE;
if (retry_after)
{
*retry_after = 0;
}
/* Process HTTP protocol version and HTTP status code */
if (!fetchline(in, &line) || !extract_token(&version, ' ', &line) ||
!match("HTTP/1.1", &version) || sscanf(line.ptr, "%d", http_code) != 1)
@@ -210,7 +214,11 @@ METHOD(est_tls_t, request, bool,
/* initialize output variables */
*out = chunk_empty;
*http_code = 0;
*retry_after = 0;
if (retry_after)
{
*retry_after = 0;
}
http = build_http_request(this, op, in);
+12 -2
View File
@@ -22,8 +22,6 @@
#ifndef EST_TLS_H_
#define EST_TLS_H_
#include "est.h"
#include <library.h>
#include <credentials/certificates/certificate.h>
@@ -32,6 +30,18 @@
typedef struct est_tls_t est_tls_t;
/**
* EST (RFC 7030) Operations
*/
typedef enum {
EST_CACERTS,
EST_SIMPLE_ENROLL,
EST_SIMPLE_REENROLL,
EST_FULL_CMC,
EST_SERVER_KEYGEN,
EST_CSR_ATTRS
} est_op_t;
/**
* TLS Interface for sending and receiving HTTPS messages
*/
+54 -16
View File
@@ -73,11 +73,15 @@ static bool print_cert_info(certificate_t *cert, pki_cert_type_t cert_type)
char digest_buf[HASH_SIZE_SHA256];
char base64_buf[HASH_SIZE_SHA256];
chunk_t cert_digest = {digest_buf, HASH_SIZE_SHA256};
chunk_t cert_id, encoding = chunk_empty;
chunk_t cert_id, serial, encoding = chunk_empty;
x509_t *x509;
bool success = FALSE;
DBG1(DBG_APP, "%s cert \"%Y\"", cert_type_label[cert_type],
cert->get_subject(cert));
x509 = (x509_t*)cert;
serial = x509->get_serial(x509);
DBG1(DBG_APP, " serial: %#B", &serial);
if (!cert->get_encoding(cert, CERT_ASN1_DER, &encoding))
{
@@ -299,26 +303,53 @@ bool pki_cert_extract_cacerts(chunk_t data, char *caout, char *raout,
{
enumerator_t *enumerator;
pkcs7_t *pkcs7 = (pkcs7_t*)container;
certificate_t *cert_found;
enumerator_t *certs;
bool trusted;
enumerator = pkcs7->create_cert_enumerator(pkcs7);
while (enumerator->enumerate(enumerator, &cert))
{
trusted = FALSE;
cert_type = get_pki_cert_type(cert);
if (cert_type == CERT_TYPE_ROOT_CA)
{
/* trust in root CA has to be established manuallly */
creds->add_cert(creds, TRUE, cert->get_ref(cert));
cert_type_count[cert_type]++;
if (!print_cert_info(cert, cert_type))
{
goto end;
}
/* same root CA as trusted TLS root CA already in cred set? */
certs = lib->credmgr->create_trusted_enumerator(lib->credmgr,
KEY_ANY, cert->get_subject(cert), FALSE);
while (certs->enumerate(certs, &cert_found, NULL))
{
if (cert->equals(cert, cert_found))
{
DBG1(DBG_APP, "Root CA equals trusted TLS Root CA");
trusted = TRUE;
break;
}
else
{
DBG1(DBG_APP, "non-matching TLS Root CA of same name");
}
}
certs->destroy(certs);
/* otherwise trust in root CA has to be established manuallly */
if (!trusted)
{
creds->add_cert(creds, TRUE, cert->get_ref(cert));
trusted = FALSE;
}
cert_type_count[cert_type]++;
if (build_pathname(&path, cert_type, cert_type_count, caout,
raout, form))
{
written = write_cert(cert, cert_type, FALSE, path, form,
written = write_cert(cert, cert_type, trusted, path, form,
force);
free(path);
}
@@ -344,24 +375,31 @@ bool pki_cert_extract_cacerts(chunk_t data, char *caout, char *raout,
while (enumerator->enumerate(enumerator, &cert))
{
written = FALSE;
trusted = FALSE;
cert_type = get_pki_cert_type(cert);
if (cert_type != CERT_TYPE_ROOT_CA)
{
certificate_t *cert_found = NULL;
enumerator_t *certs;
bool trusted;
if (!print_cert_info(cert, cert_type))
{
break;
}
/* establish trust relativ to root CA */
/* establish trust relative to root CA */
certs = lib->credmgr->create_trusted_enumerator(lib->credmgr,
KEY_ANY, cert->get_subject(cert), FALSE);
trusted = certs->enumerate(certs, &cert_found, NULL) &&
(cert_found == cert);
while (certs->enumerate(certs, &cert_found, NULL))
{
if (cert->equals(cert, cert_found))
{
trusted = TRUE;
break;
}
else
{
DBG1(DBG_APP, "non-matching TLS Sub CA of same name");
}
}
certs->destroy(certs);
cert_type_count[cert_type]++;
@@ -434,7 +472,7 @@ bool pki_cert_extract_cert(chunk_t data, cred_encoding_type_t form)
if (!(x509->get_flags(x509) & X509_CA))
{
DBG1(DBG_APP, "issued certificate \"%Y\"", cert->get_subject(cert));
DBG1(DBG_APP, "Issued certificate \"%Y\"", cert->get_subject(cert));
serial = x509->get_serial(x509);
DBG1(DBG_APP, " serial: %#B", &serial);
@@ -453,7 +491,7 @@ bool pki_cert_extract_cert(chunk_t data, cred_encoding_type_t form)
certs->destroy(certs);
valid = cert->get_validity(cert, NULL, &from, &until);
DBG1(DBG_APP, "issued certificate is %strusted, "
DBG1(DBG_APP, "Issued certificate is %strusted, "
"valid from %T until %T (currently %svalid)",
trusted ? "" : "not ", &from, FALSE, &until, FALSE,
valid ? "" : "not ");