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 = \
#include "pki.h"
#include "pki_cert.h"
-#include "est/est.h"
#include "est/est_tls.h"
#include <credentials/certificates/certificate.h>
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)
}
creds->add_cert(creds, TRUE, cacert);
continue;
- case 'o': /* --cert */
+ case 'c': /* --cert */
client_cert_file = arg;
continue;
case 'X': /* --certid */
{
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,
{"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"},
#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>
{
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;
}
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, "TLS connection to EST server was not established");
+ goto err;
+ }
+ 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))
{
- DBG1(DBG_APP, "did not receive a valid EST response: HTTP %u", http_code);
- return 1;
+ status = 0;
}
- success = pki_cert_extract_cacerts(est_response, caout, NULL, TRUE, form,
- force);
+
+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);
}
/**
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"},
{
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;
{
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)
}
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;
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;
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";
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;
}
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))
/* 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;
}
}
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,
{"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"},
{
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:
+++ /dev/null
-/*
- * 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;
-}
-
+++ /dev/null
-/*
- * 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 */
#define _GNU_SOURCE /* for asprintf() */
#include <stdio.h>
-#include <sys/types.h>
-#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
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)
{
*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)
/* 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);
#ifndef EST_TLS_H_
#define EST_TLS_H_
-#include "est.h"
-
#include <library.h>
#include <credentials/certificates/certificate.h>
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
*/
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))
{
{
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);
}
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]++;
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);
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 ");