diff --git a/docs/cli_usage.adoc b/docs/cli_usage.adoc index bd44df5..04fbee2 100644 --- a/docs/cli_usage.adoc +++ b/docs/cli_usage.adoc @@ -108,6 +108,15 @@ Same command with SAT authentication: ./certifierUtil print-cert -k -p ---- +*Fetch Sectigo Certificate* + +The certificate can be downloaded through the certificate ID returned as a result of running the command. + +---- +./certifierUtil sectigo-get-cert -C -I -r -b -A -G -E -O -J +-Z -K -u -l +---- + == *certifierUtil commands* |=== @@ -136,6 +145,12 @@ Same command with SAT authentication: | revoke | Revoke Certificate + +|sectigo-get-cert +|Requests Certificate from Sectigo + +|sectigo-help +|Provides information on implemented Sectigo commands |=== == *certifierUtil get-cert options* @@ -497,6 +512,103 @@ Disabled by default - Only error messages are shown. |=== +== *certifierUtil sectigo-get-cert options* + +|=== +| *Long Option* | *Short Option* | *Examples* | *Description* + +| help +| h +| --help + +-h +| Display this summary + +| common-name +| C +| --common-name + +-C +| Certificate common name + +| id +| I +| --id + +-I +| User or device ID + +| project-name +| r +| --project-name + +-r +| Project name + +| business-justification +| b +| --business-justification + +-b +| Business justification + +| subject-alt-names +| A +| --subject-alt-names + +-A +| Subject alternative names (CSV) + +| group-name +| G +| --group-name + +-G +| Group name + +| group-email +| E +| --group-email + +-E +| Group email + +| owner-first-name +| O +| --owner-first-name + +-O +| Owner first name + +| owner-last-name +| J +| --owner-lname + +-J +| Owner last name + +| owner-email +| Z +| --owner-email + +-Z +| Owner email + +| cert-type +| T +| --cert-type + +-T +| Certificate type + +| auth-token +| K +| --auth-token + +-K +| Sectigo API auth token + +| url +| u +| --url + +-u +| Sectigo API URL + +| config +| l +| --config + +-l +| Path to config file + +|=== + *Configuration File* Configuration File is a file used to specify internal certifier util parameters such as timeouts, ecc curve types and other miscellaneous items. This file follows the JSON Format and can be manually editted from the `libcertifier.cfg.sample` template file present in the root directory. @@ -611,4 +723,53 @@ Note: 64-bit hex integer expected as input. | Mark request for a lite certificate. + Note: value type = `bool` +| libcertifier.sectigo.url +| "https://certs.xpki.io/api/createCertificate" +| Sectigo URL + +| libcertifier.sectigo.auth.token +| "" +| Sectigo API authentication token + +| libcertifier.sectigo.common.name +| "example.com" +| Certificate common name (CN) + +| libcertifier.sectigo.group.name +| "Example Group" +| Group name for the certificate request + +| libcertifier.sectigo.group.email +| "group@example.com" +| Group email for notifications + +| libcertifier.sectigo.id +| "user123" +| User or device ID + +| libcertifier.sectigo.owner.first.name +| "First" +| Owner's first name + +| libcertifier.sectigo.owner.last.name +| "Last" +| Owner's last name + +| libcertifier.sectigo.business.justification +| "Testing" +| Business justification for the request + +| libcertifier.sectigo.subject.alt.names +| [] +| Subject alternative names. + +Note: value type = `array of strings` Pass empty array if you don't have. + +| libcertifier.sectigo.owner.email +| "owner@example.com" +| Owner's email address + +| libcertifier.sectigo.tracking.id +| "1234" +| Tracking ID for the request + |=== diff --git a/docs/configuration.adoc b/docs/configuration.adoc index b4a31d2..07191bc 100644 --- a/docs/configuration.adoc +++ b/docs/configuration.adoc @@ -2,6 +2,7 @@ xref:libcertifier.adoc[*Back to Manual*] == Configuration +== xPKI Certificates |======= | *Property Name* | *Default Value* | *Description* | libcertifier.certifier.url | https://certifier.xpki.io/v1/certifier/certificate | @@ -27,6 +28,23 @@ xref:libcertifier.adoc[*Back to Manual*] | libcertifier.ext.key.usage | clientAuth,serverAuth | (See notes below) |======= +== Sectigo Certificates +|======= +| *Property Name* | *Default Value* | *Description* +| libcertifier.sectigo.url | https://certs.xpki.io/api/createCertificate | +| libcertifier.sectigo.auth.token | | +| libcertifier.sectigo.common.name | example.com | +| libcertifier.sectigo.group.name | ExampleGroup | +| libcertifier.sectigo.group.email | group@example.com | +| libcertifier.sectigo.id | user123 | +| libcertifier.sectigo.owner.first.name | First | +| libcertifier.sectigo.owner.last.name | Last | +| libcertifier.sectigo.project.name | ExampleProject | +| libcertifier.sectigo.business.justification | Testing | +| libcertifier.sectigo.subject.alt.names | [] | +| libcertifier.sectigo.owner.email | owner@example.com | +|======= + == Extended Key Usage values: This field can be populated with a list of values, indicating purposes for which the certificate public key can be used for. diff --git a/include/certifier/certifier_api_easy.h b/include/certifier/certifier_api_easy.h index c7fc562..0037064 100644 --- a/include/certifier/certifier_api_easy.h +++ b/include/certifier/certifier_api_easy.h @@ -101,6 +101,9 @@ typedef enum CERTIFIER_MODE_PRINT_HELP = 65536, + CERTIFIER_MODE_SECTIGO_GET_CERT, + + CERTIFIER_MODE_SECTIGO_PRINT_HELP // 131072 is unused } CERTIFIER_MODE; diff --git a/include/certifier/property.h b/include/certifier/property.h index 7e01b6e..e4e9b10 100644 --- a/include/certifier/property.h +++ b/include/certifier/property.h @@ -202,6 +202,22 @@ typedef enum CERTIFIER_OPT */ CERTIFIER_OPT_MTLS_P12_PATH, CERTIFIER_OPT_MTLS_P12_PASSWORD, + + CERTIFIER_OPT_SECTIGO_AUTH_TOKEN, + CERTIFIER_OPT_SECTIGO_COMMON_NAME, + CERTIFIER_OPT_SECTIGO_GROUP_NAME, + CERTIFIER_OPT_SECTIGO_GROUP_EMAIL, + CERTIFIER_OPT_SECTIGO_ID, + CERTIFIER_OPT_SECTIGO_OWNER_FIRST_NAME, + CERTIFIER_OPT_SECTIGO_OWNER_LAST_NAME, + CERTIFIER_OPT_SECTIGO_PROJECT_NAME, + CERTIFIER_OPT_SECTIGO_BUSINESS_JUSTIFICATION, + CERTIFIER_OPT_SECTIGO_SUBJECT_ALT_NAMES, + CERTIFIER_OPT_SECTIGO_OWNER_EMAIL, + CERTIFIER_OPT_SECTIGO_URL, + CERTIFIER_OPT_SECTIGO_DEVHUB_ID, + CERTIFIER_OPT_SECTIGO_VALIDITY_DAYS, + CERTIFIER_OPT_SECTIGO_KEY_TYPE, } CERTIFIER_OPT; diff --git a/internal_headers/certifier/certifier.h b/internal_headers/certifier/certifier.h index 2204277..e2264fa 100644 --- a/internal_headers/certifier/certifier.h +++ b/internal_headers/certifier/certifier.h @@ -21,6 +21,10 @@ #include "certifier/property.h" #include "certifier/types.h" +#include "certifier/error.h" +#include "certifier/property_internal.h" + +#define SMALL_STRING_SIZE 64 #ifdef __cplusplus extern "C" { @@ -28,7 +32,6 @@ extern "C" { /* CHUNK is the size of the memory chunk used by the zlib routines. */ #define CHUNK 10000 - #define ALLOWABLE_CHARACTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnpqrstuvwxyz0123456879" #define CERTIFIER_ERR_INIT_CERTIFIER 1000 @@ -138,7 +141,23 @@ typedef enum CERTIFIER_LOG_FATAL } CertifierLogPriority; -typedef struct Certifier Certifier; +typedef struct Map +{ + char node_address[SMALL_STRING_SIZE]; + char * base64_public_key; + unsigned char * der_public_key; + int der_public_key_len; + ECC_KEY * private_ec_key; + X509_CERT * x509_cert; +} Map; + +typedef struct Certifier +{ + CertifierPropMap * prop_map; + Map tmp_map; + CertifierError last_error; + bool sectigo_mode; +} Certifier; Certifier * certifier_new(void); @@ -165,6 +184,8 @@ bool certifier_is_option_set(Certifier * certifier, int name); */ int certifier_load_cfg_file(Certifier * certifier); +int sectigo_load_cfg_file(Certifier * certifier); + char * certifier_get_version(Certifier * certifier); /** @@ -250,6 +271,10 @@ void certifier_print_certificate(Certifier * certifier, const char * pem, int pe void certifier_print_certificate_validity(Certifier * certifier); +CertifierError sectigo_generate_certificate_signing_request(Certifier *certifier, char **out_csr_pem); + +CertifierPropMap * certifier_get_prop_map(Certifier * certifier); + #ifdef __cplusplus } #endif diff --git a/internal_headers/certifier/property_internal.h b/internal_headers/certifier/property_internal.h index f1a79c6..d220e28 100644 --- a/internal_headers/certifier/property_internal.h +++ b/internal_headers/certifier/property_internal.h @@ -49,6 +49,8 @@ typedef struct _PropMap CertifierPropMap; */ CertifierPropMap * property_new(void); +CertifierPropMap * property_new_sectigo(void); + CertifierPropMap * property_ext(void); int property_destroy(CertifierPropMap * prop_map); @@ -76,18 +78,29 @@ int property_set_ext(CertifierPropMap * prop_map); int property_set(CertifierPropMap * prop_map, CERTIFIER_OPT name, const void * value); +int sectigo_property_set(CertifierPropMap * prop_map, int name, const void * value); + int property_set_int(CertifierPropMap * prop_map, CERTIFIER_OPT name, int value); void * property_get(CertifierPropMap * prop_map, CERTIFIER_OPT name); int property_set_defaults_from_cfg_file(CertifierPropMap * propMap); +int property_set_sectigo_defaults_from_cfg_file(CertifierPropMap * propMap); + const char * get_default_cfg_filename(); const char * get_default_ca_path(); const char * get_default_ca_info(); +/** + * Validate if a key type string is a supported Sectigo key type. + * @param key_type The key type string to validate + * @return 1 if valid, 0 otherwise + */ +int is_valid_sectigo_key_type(const char * key_type); + #ifdef __cplusplus } #endif diff --git a/internal_headers/certifier/sectigo_client.h b/internal_headers/certifier/sectigo_client.h new file mode 100644 index 0000000..b116781 --- /dev/null +++ b/internal_headers/certifier/sectigo_client.h @@ -0,0 +1,88 @@ +/** + * Copyright 2019 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SECTIGO_CLIENT_H +#define SECTIGO_CLIENT_H + + +#include +#include +#include +#include +#include +#include +#include +#include + +extern pthread_mutex_t lock; + +#ifdef __cplusplus +extern "C" { +#endif + + +#define IMPULSE_URL "https://certs-dev.xpki.io/" +typedef struct{ + const char * auth_token; + const char * common_name; + const char * group_name; + const char * group_email; + const char * id; + const char * owner_first_name; + const char * owner_last_name; + const char * project_name; + const char * business_justification; + const char * subject_alt_names; + const char * owner_email; + const char * sectigo_url; + const char * devhub_id; + size_t validity_days; + const char * key_type; +} sectigo_get_cert_param_t; + + +typedef enum{ + SECTIGO_CLIENT_SUCCESS = 0, + SECTIGO_CLIENT_INVALID_ARGUMENT, + SECTIGO_CLIENT_NOT_IMPLEMENTED, + SECTIGO_CLIENT_ERROR_INTERNAL, + +} SECTIGO_CLIENT_ERROR_CODE; + +typedef enum +{ + SECTIGO_AUTH_X509, + SECTIGO_AUTH_SAT, +} SECTIGO_AUTH_TYPE; + +CertifierError sectigo_client_request_certificate(CertifierPropMap * props, const unsigned char * csr, +const char * node_address, const char * certifier_id, char ** out_cert); + +CertifierError sectigo_generate_certificate_signing_request(Certifier *certifier, char **out_csr_pem); + +Certifier * get_sectigo_certifier_instance(); + +SECTIGO_CLIENT_ERROR_CODE xc_sectigo_get_cert(sectigo_get_cert_param_t * params); + +SECTIGO_CLIENT_ERROR_CODE xc_sectigo_get_default_cert_param(sectigo_get_cert_param_t * params); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libcertifier.cfg.sample b/libcertifier.cfg.sample index 570ab17..dd80ad6 100644 --- a/libcertifier.cfg.sample +++ b/libcertifier.cfg.sample @@ -2,7 +2,7 @@ "libcertifier.certifier.url": "https://certifier.xpki.io/v1/certifier", "libcertifier.profile.name": "XFN_Matter_OP_Class_3_ICA", "libcertifier.validity.days": 365, - "libcertifier.auth.type": "X509", + "libcertifier.auth.type": "x509", "libcertifier.ecc.curve.id": "prime256v1", "libcertifier.http.connect.timeout": 20, "libcertifier.http.timeout": 20, @@ -23,5 +23,21 @@ "libcertifier.product.id":"1101", "libcertifier.cn.name":"AAAAAAAA", "libcertifier.node.id":"CCCCCCCCCCCCCCCC", - "libcertifier.ext.key.usage":"critical,clientAuth,serverAuth" + "libcertifier.ext.key.usage":"critical,clientAuth,serverAuth", + + "libcertifier.sectigo.url": "https://certs.xpki.io", + "libcertifier.sectigo.auth.token": "", + "libcertifier.sectigo.common.name": "", + "libcertifier.sectigo.group.name": "", + "libcertifier.sectigo.group.email": "", + "libcertifier.sectigo.id": "", + "libcertifier.sectigo.owner.first.name": "", + "libcertifier.sectigo.owner.last.name": "", + "libcertifier.sectigo.owner.email": "", + "libcertifier.sectigo.project.name": "", + "libcertifier.sectigo.business.justification": "", + "libcertifier.sectigo.subject.alt.names": [], + "libcertifier.sectigo.devhub.id": "", + "libcertifier.sectigo.validity.days": 365, + "libcertifier.sectigo.key.type": "" } diff --git a/src/certifier.c b/src/certifier.c index 93b0f3f..4d7c904 100644 --- a/src/certifier.c +++ b/src/certifier.c @@ -31,6 +31,16 @@ #include "certifier/timer.h" #include "curl/curl.h" +#include +#include +#include +#include +#include +#include +#include "certifier/log.h" +#include "certifier/error.h" +#include "certifier/property.h" + #ifndef CERTIFIER_VERSION #define CERTIFIER_VERSION "0.1-071320 (opensource)" #endif @@ -43,22 +53,8 @@ static CERTIFIER_LOG_callback logger; -typedef struct Map -{ - char node_address[SMALL_STRING_SIZE]; - char * base64_public_key; - unsigned char * der_public_key; - int der_public_key_len; - ECC_KEY * private_ec_key; - X509_CERT * x509_cert; -} Map; - -struct Certifier -{ - CertifierPropMap * prop_map; - Map tmp_map; - CertifierError last_error; -}; + + static inline void free_tmp(Certifier * certifier); @@ -392,8 +388,6 @@ static int save_x509certs_to_filesystem(Certifier * certifier, char * x509_certs CertifierError certifier_err_info = CERTIFIER_ERROR_INITIALIZER; X509_LIST * certs = NULL; const char * password = NULL; - unsigned char *x509_der; - size_t x509_len; log_info("\nTrimming x509 certificates...\n"); util_trim(x509_certs); @@ -879,14 +873,14 @@ CertifierPropMap * _certifier_get_properties(Certifier * certifier) void _certifier_set_x509_cert(Certifier * certifier, const X509_CERT * cert) { security_free_cert(certifier->tmp_map.x509_cert); - X509_CERT * tmp = NULL; + const X509_CERT * tmp = NULL; if (cert != NULL) { tmp = cert; } - certifier->tmp_map.x509_cert = tmp; + certifier->tmp_map.x509_cert = (X509_CERT *)tmp; } void _certifier_set_ecc_key(Certifier * certifier, const ECC_KEY * key) @@ -1012,9 +1006,15 @@ int certifier_set_property(Certifier * certifier, int name, const void * value) NULL_CHECK(certifier); int return_code = 0; - const void * origValue = property_get(certifier->prop_map, name); - return_code = property_set(certifier->prop_map, name, value); + if (certifier->sectigo_mode) { + // Only set Sectigo properties for Sectigo logical flow + return_code = sectigo_property_set(certifier->prop_map, name, value); + } else { + // Only set XPKI properties for XPKI logical flow + return_code = property_set(certifier->prop_map, name, value); + } + if (return_code != 0) { return CERTIFIER_ERR_PROPERTY_SET + return_code; @@ -1022,38 +1022,34 @@ int certifier_set_property(Certifier * certifier, int name, const void * value) switch (name) { - case CERTIFIER_OPT_CFG_FILENAME: { + case CERTIFIER_OPT_CFG_FILENAME: + { log_info("Configuration file changed; loading settings"); - /* Blow away all settings and reload from config to avoid mixed configs */ - CertifierPropMap * orig = certifier->prop_map; - certifier->prop_map = property_new(); - if (certifier->prop_map == NULL) - { - log_error("Could not allocate enough memory to construct certifier->prop_map"); - return CERTIFIER_ERR_PROPERTY_SET_MEMORY; - } - property_set(certifier->prop_map, name, value); - - if (value != NULL) - { - return_code = certifier_load_cfg_file(certifier); - } - else - { - return_code = 0; - } + CertifierPropMap *orig = certifier->prop_map; - if (return_code == 0) - { - property_destroy(orig); - } - else - { - property_destroy(certifier->prop_map); - certifier->prop_map = orig; - return_code = property_set(certifier->prop_map, name, origValue); - log_warn("Failed to load configuration (configuration unmodified)!"); + if (value != NULL) { + if (certifier->sectigo_mode) { + + return_code = sectigo_load_cfg_file(certifier); + if (return_code != 0) { + log_warn("Failed to load Sectigo configuration!"); + return CERTIFIER_ERR_PROPERTY_SET + return_code; + } + } else { + return_code = certifier_load_cfg_file(certifier); + if (return_code == 0) { + + property_destroy(orig); + + } else { + property_destroy(certifier->prop_map); + certifier->prop_map = orig; + return_code = property_set(certifier->prop_map, name, property_get(orig, name)); + log_warn("Failed to load configuration (configuration unmodified)!"); + return CERTIFIER_ERR_PROPERTY_SET + return_code; + } + } } break; @@ -1115,6 +1111,23 @@ int certifier_load_cfg_file(Certifier * certifier) return return_code; } +int sectigo_load_cfg_file(Certifier * certifier) +{ + NULL_CHECK(certifier); + + int return_code = 0; + + // Only set Sectigo keys from config + return_code = property_set_sectigo_defaults_from_cfg_file(certifier->prop_map); + + if (return_code != 0) + { + return_code = CERTIFIER_ERR_PROPERTY_SET + return_code; + } + + return return_code; +} + char * certifier_create_info(Certifier * certifier, const int return_code, const char * output) { if (certifier == NULL) @@ -1541,3 +1554,72 @@ char * certifier_create_csr_post_data(CertifierPropMap * props, const unsigned c return json_csr; } + +CertifierError sectigo_generate_certificate_signing_request(Certifier *certifier, char **out_csr_pem) { + CertifierError rc = CERTIFIER_ERROR_INITIALIZER; + EVP_PKEY *pkey = NULL; + X509_REQ *req = NULL; + X509_NAME *name = NULL; + BIO *bio = NULL; + BUF_MEM *bptr = NULL; + char *common_name = (char *)certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_COMMON_NAME); + + if (!common_name) { + set_last_error(certifier, 14, "Common Name not set"); + rc.application_error_code = 14; + rc.application_error_msg = "Common Name not set"; + return rc; + } + + + pkey = EVP_PKEY_new(); + RSA *rsa = RSA_new(); + BIGNUM *bn = BN_new(); + BN_set_word(bn, RSA_F4); + + RSA_generate_key_ex(rsa, 2048, bn, NULL); + EVP_PKEY_assign_RSA(pkey, rsa); + BN_free(bn); + + + req = X509_REQ_new(); + X509_REQ_set_pubkey(req, pkey); + + name = X509_NAME_new(); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char*)common_name, -1, -1, 0); + X509_REQ_set_subject_name(req, name); + + if (!X509_REQ_sign(req, pkey, EVP_sha256())) { + set_last_error(certifier, 15, "Error signing CSR"); + rc.application_error_code = 15; + rc.application_error_msg = "Error signing CSR"; + goto cleanup; + } + + //CSR to PEM + bio = BIO_new(BIO_s_mem()); + PEM_write_bio_X509_REQ(bio, req); + BIO_get_mem_ptr(bio, &bptr); + + *out_csr_pem = (char *)malloc(bptr->length + 1); + memcpy(*out_csr_pem, bptr->data, bptr->length); + (*out_csr_pem)[bptr->length] = '\0'; + + rc.application_error_code = 0; + rc.application_error_msg = NULL; + +cleanup: + if (bio) BIO_free(bio); + if (req) X509_REQ_free(req); + if (name) X509_NAME_free(name); + if (pkey) EVP_PKEY_free(pkey); + return rc; +} + +CertifierPropMap * certifier_get_prop_map(Certifier * certifier) +{ + if (certifier == NULL) { + return NULL; + } + return certifier->prop_map; +} \ No newline at end of file diff --git a/src/certifier_api_easy.c b/src/certifier_api_easy.c index a0ef341..851960d 100644 --- a/src/certifier_api_easy.c +++ b/src/certifier_api_easy.c @@ -26,6 +26,8 @@ #include "certifier/security.h" #include "certifier/types.h" #include "certifier/util.h" +#include "certifier/sectigo_client.h" +#include "certifier/property_internal.h" #include #include @@ -56,6 +58,7 @@ #define GET_CERT_SHORT_OPTIONS "fT:P:o:i:n:F:a:w:" #define VALIDITY_DAYS_SHORT_OPTION "t:" #define CA_PATH_SHORT_OPTION "c:" +#define SECTIGO_GET_CERT_SHORT_OPTIONS "C:I:e:s:N:r:b:A:x:K:u:G:E:O:J:Z:U:T:l:W:S:h" #define BASE_LONG_OPTIONS \ { "help", no_argument, NULL, 'h' }, { "input-p12-path", required_argument, NULL, 'k' }, \ @@ -90,6 +93,24 @@ "ca-path", required_argument, NULL, 'c' \ } +#define SECTIGO_GET_CERT_LONG_OPTIONS \ + { "common-name", required_argument, NULL, 'C' }, \ + { "id", required_argument, NULL, 'I' }, \ + { "project-name", required_argument, NULL, 'r' }, \ + { "business-justification", required_argument, NULL, 'b' }, \ + { "subject-alt-names", required_argument, NULL, 'A' }, \ + {"url", required_argument, NULL, 'u'}, \ + { "auth-token", required_argument, NULL, 'K' }, \ + { "group-name", required_argument, NULL, 'G' }, \ + { "group-email", required_argument, NULL, 'E' }, \ + { "owner-first-name", required_argument, NULL, 'O' }, \ + { "owner-last-name", required_argument, NULL, 'J' }, \ + { "owner-email", required_argument, NULL, 'Z' }, \ + { "config", required_argument, NULL, 'l' }, \ + { NULL, 0, NULL, 0 } + //make default arg '*' for san and ip + //only take in choices=['fte', 'contractor', 'associate'] + static void finish_operation(CERTIFIER * easy, int return_code, const char * operation_output); // Private data @@ -130,7 +151,7 @@ static size_t get_command_opt_index(command_opt_lut_t * command_opt_lut, size_t return -1; } -static const char * get_command_opt_helper(CERTIFIER_MODE mode) +static const char * get_xpki_command_opt_helper(CERTIFIER_MODE mode) { #define BASE_HELPER \ "Usage: certifierUtil %s [OPTIONS]\n" \ @@ -304,6 +325,7 @@ CERTIFIER_MODE certifier_api_easy_get_mode(CERTIFIER * easy) { "renew-cert", CERTIFIER_MODE_RENEW_CERT }, { "print-cert", CERTIFIER_MODE_PRINT_CERT }, { "revoke", CERTIFIER_MODE_REVOKE_CERT }, + { "sectigo-get-cert", CERTIFIER_MODE_SECTIGO_GET_CERT} }; for (int i = 0; i < sizeof(command_map) / sizeof(command_map_t); ++i) @@ -739,6 +761,56 @@ static int do_print_cert(CERTIFIER * easy) return return_code; } +static int do_sectigo_get_cert(CERTIFIER * easy) +{ + int return_code = 0; + char * csr_pem = NULL; + char * cert = NULL; + + // Check for required Sectigo properties + const char *common_name = certifier_get_property(easy->certifier, CERTIFIER_OPT_SECTIGO_COMMON_NAME); + const char *project_name = certifier_get_property(easy->certifier, CERTIFIER_OPT_SECTIGO_PROJECT_NAME); + const char *business_justification = certifier_get_property(easy->certifier, CERTIFIER_OPT_SECTIGO_BUSINESS_JUSTIFICATION); + + if (util_is_empty(common_name) || util_is_empty(business_justification) || util_is_empty(project_name)) { + + finish_operation(easy, CERTIFIER_ERR_EMPTY_OR_INVALID_PARAM_1, + "Missing required Sectigo flags (common-name, project-name, business-justification)"); + return CERTIFIER_ERR_EMPTY_OR_INVALID_PARAM_1; + } + + return_code = certifier_setup_keys(easy->certifier); + if (return_code != 0) { + finish_operation(easy, return_code, NULL); + return return_code; + } + + //Generate CSR + CertifierError rc = sectigo_generate_certificate_signing_request(easy->certifier, &csr_pem); + if (rc.application_error_code != 0 || csr_pem == NULL) { + finish_operation(easy, rc.application_error_code, NULL); + return rc.application_error_code; + } + + // Call Sectigo API to request certificate + CertifierPropMap * props = certifier_easy_api_get_props(easy->certifier); + rc = sectigo_client_request_certificate(props, (unsigned char *)csr_pem, certifier_get_node_address(easy->certifier), NULL, &cert); + + XFREE(csr_pem); + + //Handle result + if (rc.application_error_code == 0 && cert != NULL) { + finish_operation(easy, 0, cert); + XFREE(cert); + return 0; + } else { + finish_operation(easy, rc.application_error_code, rc.application_error_msg); + if (cert) XFREE(cert); + return rc.application_error_code; + } +} + + char * certifier_api_easy_get_version(CERTIFIER * easy) { if (easy == NULL) @@ -777,7 +849,8 @@ int certifier_api_easy_print_helper(CERTIFIER * easy) "get-cert-status\n" "renew-cert\n" "print-cert\n" - "revoke\n"); + "revoke\n" + "get-sectigo-cert\n"); } return 0; @@ -826,7 +899,8 @@ static int process_command_line(CERTIFIER * easy) static const char * const get_cert_status_short_options = BASE_SHORT_OPTIONS CA_PATH_SHORT_OPTION; static const char * const renew_cert_short_options = BASE_SHORT_OPTIONS CA_PATH_SHORT_OPTION; static const char * const print_cert_short_options = BASE_SHORT_OPTIONS; - static const char * const revoke_cert_short_options = BASE_SHORT_OPTIONS CA_PATH_SHORT_OPTION; + static const char * const revoke_cert_short_options = BASE_SHORT_OPTIONS; + static const char * const sectigo_get_cert_short_options = BASE_SHORT_OPTIONS CA_PATH_SHORT_OPTION; static const struct option get_cert_long_opts[] = { BASE_LONG_OPTIONS, GET_CRT_TOKEN_LONG_OPTIONS, GET_CERT_LONG_OPTIONS, VALIDITY_DAYS_LONG_OPTION, @@ -836,6 +910,7 @@ static int process_command_line(CERTIFIER * easy) static const struct option renew_cert_long_opts[] = { BASE_LONG_OPTIONS, CA_PATH_LONG_OPTION, { NULL, 0, NULL, 0 } }; static const struct option print_cert_long_opts[] = { BASE_LONG_OPTIONS, { NULL, 0, NULL, 0 } }; static const struct option revoke_cert_long_opts[] = { BASE_LONG_OPTIONS, CA_PATH_LONG_OPTION, { NULL, 0, NULL, 0 } }; + static const struct option sectigo_get_cert_long_opts[] = {BASE_LONG_OPTIONS, SECTIGO_GET_CERT_LONG_OPTIONS, {NULL, 0, NULL, 0}}; static command_opt_lut_t command_opt_lut[] = { { CERTIFIER_MODE_REGISTER, get_cert_short_options, get_cert_long_opts }, @@ -844,6 +919,7 @@ static int process_command_line(CERTIFIER * easy) { CERTIFIER_MODE_RENEW_CERT, renew_cert_short_options, renew_cert_long_opts }, { CERTIFIER_MODE_PRINT_CERT, print_cert_short_options, print_cert_long_opts }, { CERTIFIER_MODE_REVOKE_CERT, revoke_cert_short_options, revoke_cert_long_opts }, + {CERTIFIER_MODE_SECTIGO_GET_CERT, sectigo_get_cert_short_options, sectigo_get_cert_long_opts} }; char * version_string = certifier_api_easy_get_version(easy); @@ -869,7 +945,7 @@ static int process_command_line(CERTIFIER * easy) switch (opt) { case 'h': - XFPRINTF(stdout, get_command_opt_helper(easy->mode), easy->argv[0]); + XFPRINTF(stdout, get_xpki_command_opt_helper(easy->mode), easy->argv[0]); exit(1); case 'c': return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_CA_PATH, optarg); @@ -1067,6 +1143,92 @@ static int process_command_line(CERTIFIER * easy) case 'v': return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_LOG_LEVEL, (void *) (size_t) 0); break; + case 'C': // common-name + if (optarg) + { + return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_COMMON_NAME, optarg); + } + break; + case 'I': // id + if (optarg) + { + return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_ID, optarg); + } + break; + case 'r': // project-name + if (optarg) { + return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_PROJECT_NAME, optarg); + } + break; + case 'b': // business-justification + if (optarg) { + return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_BUSINESS_JUSTIFICATION, optarg); + } + break; + case 'A': // subject-alt-names + if (optarg) { + return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_SUBJECT_ALT_NAMES, optarg); + } + break; + case 'K': // auth-token + if (optarg) { + return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_AUTH_TOKEN, optarg); + } + break; + case 'u': // sectigo url + if (optarg) { + return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_URL, optarg); + } + case 'G': // group-name + if (optarg) { + return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_GROUP_NAME, optarg); + } + break; + case 'E': // group-email + if (optarg) { + return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_GROUP_EMAIL, optarg); + } + break; + case 'O': // owner-first-name + if (optarg) { + return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_OWNER_FIRST_NAME, optarg); + } + break; + case 'J': // owner-last-name + if (optarg) { + return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_OWNER_LAST_NAME, optarg); + } + break; + case 'M': // owner-email + if (optarg) { + return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_OWNER_EMAIL, optarg); + } + break; + case 'D': // DevHub ID + if (optarg) { + return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_DEVHUB_ID, optarg); + } + break; + case 'V': // Validity Days for Sectigo cert + if (optarg) { + if (atoi(optarg) > 0) + { + return_code = + certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_VALIDITY_DAYS, (const void *) (size_t) atoi(optarg)); + } + else + { + log_error("Expected input to be of positive integer type"); + return_code = 1; + } + } + break; + case 'W': // Key Type + if (!is_valid_sectigo_key_type(optarg)) { + log_error("Invalid key type. Supported key types: [RSA-2048, RSA-3072, RSA-4096, RSA-8192, ECC-PRIME256V1, ECC-SECP384R1]"); + exit(0); + } + return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_SECTIGO_KEY_TYPE, optarg); case '?': /* Case when user enters the command as * $ ./libCertifier -p @@ -1343,10 +1505,26 @@ int certifier_api_easy_perform(CERTIFIER * easy) break; } + //For SECTIGO MODE +switch(easy -> mode){ + case CERTIFIER_MODE_NONE: + break; + + case CERTIFIER_MODE_SECTIGO_GET_CERT: + do_sectigo_get_cert(easy); + break; + + default: + finish_operation(easy, -1, "Invalid mode"); + break; +} + cleanup: return easy->last_info.error_code; } + + http_response * certifier_api_easy_http_post(const CERTIFIER * easy, const char * url, const char * http_headers[], const char * csr) { diff --git a/src/http.c b/src/http.c index 3db22fb..7fc5b67 100644 --- a/src/http.c +++ b/src/http.c @@ -34,10 +34,10 @@ static void set_curl_options(CURL * curl, CertifierPropMap * prop_map) char * mtls_p12 = property_get(prop_map, CERTIFIER_OPT_MTLS_P12_PATH); char * mtls_password = property_get(prop_map, CERTIFIER_OPT_MTLS_P12_PASSWORD); - log_debug("[set_curl_options] - Host Validation=%i", host_validation); - log_debug("[set_curl_options] - Peer Validation=%i", peer_validation); - log_debug("[set_curl_options] - Debug HTTP Enabled=%i", is_debug_http_enabled); - log_debug("[set_curl_options] - Trace HTTP Enabled=%i", is_trace_http_enabled); + log_debug("[set_curl_options] - Host Validation=%i\n", host_validation); + log_debug("[set_curl_options] - Peer Validation=%i\n", peer_validation); + log_debug("[set_curl_options] - Debug HTTP Enabled=%i\n", is_debug_http_enabled); + log_debug("[set_curl_options] - Trace HTTP Enabled=%i\n", is_trace_http_enabled); // First set the URL that is about to receive our POST. http_set_curlopt(curl, CURLOPT_ACCEPT_ENCODING, ""); @@ -57,7 +57,6 @@ static void set_curl_options(CURL * curl, CertifierPropMap * prop_map) http_set_curlopt(curl, CURLOPT_SSL_VERIFYHOST, host_validation); http_set_curlopt(curl, CURLOPT_SSL_VERIFYPEER, peer_validation); - http_set_curlopt(curl, CURLOPT_FAILONERROR, 1L); http_set_curlopt(curl, CURLOPT_TIMEOUT, http_timeout); http_set_curlopt(curl, CURLOPT_CONNECTTIMEOUT, http_connect_timeout); diff --git a/src/main.c b/src/main.c index 72a106f..c3b8126 100644 --- a/src/main.c +++ b/src/main.c @@ -20,13 +20,10 @@ #include "certifier/log.h" #include "certifier/xpki_client.h" #include "certifier/xpki_client_internal.h" - -XPKI_CLIENT_ERROR_CODE xpki_perform(int argc, char ** argv); - -int main(int argc, char ** argv) -{ - return xpki_perform(argc, argv); -} +#include "certifier/sectigo_client.h" +#include "certifier/certifier_api_easy.h" +#include "certifier/certifier_internal.h" +#include "certifier/certifier.h" typedef enum { @@ -40,6 +37,14 @@ typedef enum XPKI_MODE_REVOKE_CERT, } XPKI_MODE; +typedef enum +{ + SECTIGO_MODE_NONE, + SECTIGO_MODE_GET_CERT, + SECTIGO_MODE_PRINT_HELP + +} SECTIGO_MODE; + typedef union { get_cert_param_t get_cert_param; @@ -47,6 +52,29 @@ typedef union renew_cert_param_t renew_cert_param; } xc_parameter_t; +typedef union +{ + sectigo_get_cert_param_t get_cert_param; +} sectigo_parameter_t; + + +XPKI_CLIENT_ERROR_CODE process(XPKI_MODE mode, xc_parameter_t * xc_parameter, int argc, char ** argv); +XPKI_CLIENT_ERROR_CODE xpki_perform(int argc, char ** argv); +SECTIGO_CLIENT_ERROR_CODE sectigo_perform(int argc, char ** argv); + +int main(int argc, char **argv) +{ + pthread_mutex_init(&lock, NULL); + // check for "sectigo-get-cert" as the first argument + if (argc > 1 && strncmp(argv[1], "sectigo", strlen("sectigo")) == 0) { + // Call Sectigo mode + return sectigo_perform(argc, argv); + } else { + // Default to XPKI mode + return xpki_perform(argc, argv); + } +} + XPKI_MODE xpki_get_mode(int argc, char ** argv) { if (argc <= 1 && argv[1] == NULL) @@ -64,7 +92,7 @@ XPKI_MODE xpki_get_mode(int argc, char ** argv) { "help", XPKI_MODE_PRINT_HELP }, { "version", XPKI_MODE_PRINT_VERSION }, { "get-cert", XPKI_MODE_GET_CERT }, { "get-cert-status", XPKI_MODE_GET_CERT_STATUS }, { "renew-cert", XPKI_MODE_RENEW_CERT }, { "print-cert", XPKI_MODE_PRINT_CERT }, - { "revoke", XPKI_CLIENT_CERT_REVOKED }, + { "revoke", XPKI_MODE_REVOKE_CERT }, }; for (int i = 0; i < sizeof(command_map) / sizeof(command_map_t); ++i) @@ -78,6 +106,27 @@ XPKI_MODE xpki_get_mode(int argc, char ** argv) return XPKI_MODE_NONE; } +SECTIGO_MODE sectigo_get_mode(int argc, char ** argv){ + typedef struct{ + char * name; + SECTIGO_MODE mode; + } command_map_t; + + command_map_t command_map[] = { + {"sectigo-help", SECTIGO_MODE_PRINT_HELP}, {"sectigo-get-cert", SECTIGO_MODE_GET_CERT} + }; + + for(int i = 0; i < sizeof(command_map) / sizeof(command_map_t); ++i){ + if (strcmp(argv[1], command_map[i].name) == 0){ + return command_map[i].mode; + } + + } + + + return SECTIGO_MODE_NONE; +} + XPKI_CLIENT_ERROR_CODE xpki_print_helper(XPKI_MODE mode) { if (mode == XPKI_MODE_PRINT_VERSION) @@ -105,17 +154,34 @@ XPKI_CLIENT_ERROR_CODE xpki_print_helper(XPKI_MODE mode) "get-cert-status\n" "renew-cert\n" "print-cert\n" - "revoke\n"); + "revoke\n" + "sectigo-get-cert\n" + "sectigo-help\n"); } return XPKI_CLIENT_SUCCESS; } +SECTIGO_CLIENT_ERROR_CODE sectigo_print_helper(SECTIGO_MODE mode) +{ + if (mode == SECTIGO_MODE_PRINT_HELP || mode == SECTIGO_MODE_NONE) + { + XFPRINTF(stdout, + "Usage: certifierUtil [COMMANDS] [OPTIONS]\n" + "Commands:\n" + "help\n" + "sectigo-get-cert\n"); + } + + return SECTIGO_CLIENT_SUCCESS; +} + #define BASE_SHORT_OPTIONS "hp:L:k:vm" #define GET_CRT_TOKEN_SHORT_OPTIONS "X:S:" #define GET_CERT_SHORT_OPTIONS "fT:P:o:i:n:F:a:w:" #define VALIDITY_DAYS_SHORT_OPTION "t:" #define CA_PATH_SHORT_OPTION "c:" +#define SECTIGO_GET_CERT_SHORT_OPTIONS "C:I:e:s:N:r:b:A:x:K:u:G:E:O:J:Z:U:T:l:W" #define BASE_LONG_OPTIONS \ { "help", no_argument, NULL, 'h' }, { "input-p12-path", required_argument, NULL, 'k' }, \ @@ -150,6 +216,24 @@ XPKI_CLIENT_ERROR_CODE xpki_print_helper(XPKI_MODE mode) "ca-path", required_argument, NULL, 'c' \ } +#define SECTIGO_GET_CERT_LONG_OPTIONS \ + { "common-name", required_argument, NULL, 'C' }, \ + { "id", required_argument, NULL, 'I' }, \ + { "project-name", required_argument, NULL, 'r' }, \ + { "business-justification", required_argument, NULL, 'b' }, \ + { "subject-alt-names", required_argument, NULL, 'A' }, \ + {"url", required_argument, NULL, 'u'}, \ + { "auth-token", required_argument, NULL, 'K' }, \ + { "group-name", required_argument, NULL, 'G' }, \ + { "group-email", required_argument, NULL, 'E' }, \ + { "owner-first-name", required_argument, NULL, 'O' }, \ + { "owner-last-name", required_argument, NULL, 'J' }, \ + { "owner-email", required_argument, NULL, 'Z' }, \ + { "config", required_argument, NULL, 'l' }, \ + { NULL, 0, NULL, 0 } \ + //make default arg '*' for san and ip + //only take in choices=['fte', 'contractor', 'associate'] + typedef struct { XPKI_MODE mode; @@ -157,6 +241,13 @@ typedef struct const struct option * long_opts; } command_opt_lut_t; +typedef struct +{ + SECTIGO_MODE mode; + const char * short_opts; + const struct option * long_opts; +} sectigo_command_opt_lut_t; + static size_t get_command_opt_index(command_opt_lut_t * command_opt_lut, size_t n_entries, XPKI_MODE mode) { for (size_t i = 0; i < n_entries; ++i) @@ -169,7 +260,42 @@ static size_t get_command_opt_index(command_opt_lut_t * command_opt_lut, size_t return -1; } -static const char * get_command_opt_helper(XPKI_MODE mode) +static const char * get_sectigo_command_opt_helper(SECTIGO_MODE mode) +{ + +#define SECTIGO_BASE_HELPER \ + "Usage: certifierUtil sectigo-get-cert [OPTIONS]\n" + +#define SECTIGO_GET_CERT_HELPER \ + "--common-name [value] (-C)\n" \ + "--id [value] (-I)\n" \ + "--project-name [value] (-r)\n" \ + "--business-justification [value] (-b)\n" \ + "--subject-alt-names [value] (-A)\n" \ + "--group-name [value] (-G)\n" \ + "--group-email [value] (-E)\n" \ + "--owner-first-name [value] (-O)\n" \ + "--owner-last-name [value] (-J)\n" \ + "--owner-email [value] (-Z)\n" \ + "--devhub-id [value] (-D)\n" \ + "--validity-days [value] (-V)\n" \ + "--key-type [value] (-W)\n" \ + "--auth-token [value] (-K)\n" \ + "--url [value] (-u)\n" \ + "--config [value] (-l)\n" \ + + switch (mode) + { + case SECTIGO_MODE_GET_CERT: + return SECTIGO_BASE_HELPER SECTIGO_GET_CERT_HELPER; + case SECTIGO_MODE_PRINT_HELP: + return SECTIGO_BASE_HELPER; + default: + return ""; + } +} + +static const char * get_xpki_command_opt_helper(XPKI_MODE mode) { #define BASE_HELPER \ "Usage: certifierUtil %s [OPTIONS]\n" \ @@ -234,8 +360,7 @@ XPKI_CLIENT_ERROR_CODE process(XPKI_MODE mode, xc_parameter_t * xc_parameter, in break; default: return XPKI_CLIENT_NOT_IMPLEMENTED; - } - + } static const char * const get_cert_short_options = BASE_SHORT_OPTIONS GET_CRT_TOKEN_SHORT_OPTIONS GET_CERT_SHORT_OPTIONS VALIDITY_DAYS_SHORT_OPTION CA_PATH_SHORT_OPTION; static const char * const get_cert_status_short_options = BASE_SHORT_OPTIONS CA_PATH_SHORT_OPTION; @@ -270,9 +395,8 @@ XPKI_CLIENT_ERROR_CODE process(XPKI_MODE mode, xc_parameter_t * xc_parameter, in switch (opt) { case 'h': - XFPRINTF(stdout, get_command_opt_helper(mode), argv[0]); + XFPRINTF(stdout, get_xpki_command_opt_helper(mode), argv[0]); exit(0); - break; case 'c': // return_code = certifier_set_property(easy->certifier, CERTIFIER_OPT_CA_PATH, optarg); break; @@ -393,7 +517,7 @@ XPKI_CLIENT_ERROR_CODE process(XPKI_MODE mode, xc_parameter_t * xc_parameter, in } else if (optopt == 'D') { - log_info("Missing mandatory custom property option"); + log_info("Missing mandatory custom property option"); error_code = XPKI_CLIENT_INVALID_ARGUMENT; break; } @@ -451,6 +575,160 @@ XPKI_CLIENT_ERROR_CODE process(XPKI_MODE mode, xc_parameter_t * xc_parameter, in return error_code; } +// --- Sectigo Option Table --- +static const char * const sectigo_get_cert_short_options = "C:I:e:s:N:r:b:A:x:K:u:G:E:O:J:Z:U:T:l:W:V:D:h"; +static const struct option sectigo_get_cert_long_opts[] = { + { "common-name", required_argument, NULL, 'C' }, + { "id", required_argument, NULL, 'I' }, + { "project-name", required_argument, NULL, 'r' }, + { "business-justification", required_argument, NULL, 'b' }, + { "subject-alt-names", required_argument, NULL, 'A' }, + { "url", required_argument, NULL, 'u'}, + { "auth-token", required_argument, NULL, 'K' }, + { "group-name", required_argument, NULL, 'G' }, + { "group-email", required_argument, NULL, 'E' }, + { "owner-first-name", required_argument, NULL, 'O' }, + { "owner-last-name", required_argument, NULL, 'J' }, + { "owner-email", required_argument, NULL, 'Z' }, + { "devhub-id", required_argument, NULL, 'D' }, + { "validity-days", required_argument, NULL, 'V' }, + { "key-type", required_argument, NULL, 'W' }, + { "config", required_argument, NULL, 'l' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + //make default arg '*' for san and ip + //only take in choices=['fte', 'contractor', 'associate'] +}; + +// --- Sectigo Option Parsing --- +SECTIGO_CLIENT_ERROR_CODE sectigo_process(SECTIGO_MODE mode, sectigo_parameter_t * sectigo_parameter, int argc, char ** argv) +{ + VerifyOrReturnError(sectigo_parameter != NULL, SECTIGO_CLIENT_INVALID_ARGUMENT); + VerifyOrReturnError(argv != NULL, SECTIGO_CLIENT_INVALID_ARGUMENT); + + SECTIGO_CLIENT_ERROR_CODE error_code = SECTIGO_CLIENT_SUCCESS; + switch (mode) + { + case SECTIGO_MODE_GET_CERT: + ReturnErrorOnFailure(xc_sectigo_get_default_cert_param(§igo_parameter->get_cert_param)); + break; + default: + return SECTIGO_CLIENT_NOT_IMPLEMENTED; + } + + for (;;) + { + int option_index; + int opt = XGETOPT_LONG(argc, argv, sectigo_get_cert_short_options, + sectigo_get_cert_long_opts, &option_index); + + if (opt == -1 || error_code != SECTIGO_CLIENT_SUCCESS) + { + break; + } + + switch (opt) + { + case 'h': + XFPRINTF(stdout, get_sectigo_command_opt_helper(mode), argv[0]); + exit(0); + case 'C': + sectigo_parameter->get_cert_param.common_name = optarg; + certifier_set_property(get_sectigo_certifier_instance(), CERTIFIER_OPT_SECTIGO_COMMON_NAME, optarg); + break; + case 'I': + sectigo_parameter->get_cert_param.id = optarg; + certifier_set_property(get_sectigo_certifier_instance(), CERTIFIER_OPT_SECTIGO_ID, optarg); + break; + case 'b': + sectigo_parameter->get_cert_param.business_justification = optarg; + certifier_set_property(get_sectigo_certifier_instance(), CERTIFIER_OPT_SECTIGO_BUSINESS_JUSTIFICATION, optarg); + break; + case 'A': + sectigo_parameter->get_cert_param.subject_alt_names = optarg; + certifier_set_property(get_sectigo_certifier_instance(), CERTIFIER_OPT_SECTIGO_SUBJECT_ALT_NAMES, optarg); + break; + case 'l': + // config file path, handled in sectigo_perform + break; + case 'G': + sectigo_parameter->get_cert_param.group_name = optarg; + certifier_set_property(get_sectigo_certifier_instance(), CERTIFIER_OPT_SECTIGO_GROUP_NAME, optarg); + break; + case 'E': + sectigo_parameter->get_cert_param.group_email = optarg; + certifier_set_property(get_sectigo_certifier_instance(), CERTIFIER_OPT_SECTIGO_GROUP_EMAIL, optarg); + break; + case 'O': + sectigo_parameter->get_cert_param.owner_first_name = optarg; + certifier_set_property(get_sectigo_certifier_instance(), CERTIFIER_OPT_SECTIGO_OWNER_FIRST_NAME, optarg); + break; + case 'J': + sectigo_parameter->get_cert_param.owner_last_name = optarg; + certifier_set_property(get_sectigo_certifier_instance(), CERTIFIER_OPT_SECTIGO_OWNER_LAST_NAME, optarg); + break; + case 'Z': + sectigo_parameter->get_cert_param.owner_email = optarg; + certifier_set_property(get_sectigo_certifier_instance(), CERTIFIER_OPT_SECTIGO_OWNER_EMAIL, optarg); + break; + case 'K': + sectigo_parameter->get_cert_param.auth_token = optarg; + certifier_set_property(get_sectigo_certifier_instance(), CERTIFIER_OPT_SECTIGO_AUTH_TOKEN, optarg); + break; + case 'u': + sectigo_parameter->get_cert_param.sectigo_url = optarg; + certifier_set_property(get_sectigo_certifier_instance(), CERTIFIER_OPT_SECTIGO_URL, optarg); + break; + case 'D': + sectigo_parameter->get_cert_param.devhub_id = optarg; + certifier_set_property(get_sectigo_certifier_instance(), CERTIFIER_OPT_SECTIGO_DEVHUB_ID, optarg); + break; + case 'V': + sectigo_parameter->get_cert_param.validity_days = atol(optarg); + certifier_set_property(get_sectigo_certifier_instance(), CERTIFIER_OPT_SECTIGO_VALIDITY_DAYS, (void *)(size_t)atol(optarg)); + break; + case 'W': + sectigo_parameter->get_cert_param.key_type = optarg; + certifier_set_property(get_sectigo_certifier_instance(), CERTIFIER_OPT_SECTIGO_KEY_TYPE, optarg); + break; + case '?': + log_info("Invalid or missing Sectigo option"); + error_code = SECTIGO_CLIENT_INVALID_ARGUMENT; + break; + default: + log_info("Unknown Sectigo option: %c", opt); + error_code = SECTIGO_CLIENT_INVALID_ARGUMENT; + break; + } + } + + return error_code; +} + +SECTIGO_CLIENT_ERROR_CODE sectigo_perform(int argc, char ** argv) +{ + SECTIGO_MODE mode = sectigo_get_mode(argc, argv); + + if (mode == SECTIGO_MODE_NONE || mode == SECTIGO_MODE_PRINT_HELP) + { + return sectigo_print_helper(mode); + } + + sectigo_parameter_t sectigo_parameter; + + ReturnErrorOnFailure(sectigo_process(mode, §igo_parameter, argc - 1, &argv[1])); + + switch (mode) + { + case SECTIGO_MODE_GET_CERT: + return xc_sectigo_get_cert(§igo_parameter.get_cert_param); + break; + default: + break; + } + return SECTIGO_CLIENT_SUCCESS; +} + XPKI_CLIENT_ERROR_CODE xpki_perform(int argc, char ** argv) { XPKI_MODE mode = xpki_get_mode(argc, argv); @@ -472,7 +750,7 @@ XPKI_CLIENT_ERROR_CODE xpki_perform(int argc, char ** argv) case XPKI_MODE_GET_CERT_STATUS: { XPKI_CLIENT_CERT_STATUS status; ReturnErrorOnFailure(xc_get_cert_status(&xc_parameter.get_cert_status_param, &status)); - return status; + return (XPKI_CLIENT_ERROR_CODE)status; } break; case XPKI_MODE_RENEW_CERT: diff --git a/src/property.c b/src/property.c index 8413ed6..0ffe906 100644 --- a/src/property.c +++ b/src/property.c @@ -36,6 +36,27 @@ #define DEFAULT_OUTPUT_P12_PATH "output.p12" #define DEFAULT_CFG_FILENAME "libcertifier.cfg" #define DEFAULT_USER_CFG_FILENAME "/usr/local/etc/certifier/libcertifier.cfg" + +// Helper function to validate Sectigo key type +int is_valid_sectigo_key_type(const char *key_type) +{ + if (!key_type) { + return 0; + } + + const char *valid_key_types[] = { + "RSA-2048", "RSA-3072", "RSA-4096", "RSA-8192", + "ECC-PRIME256V1", "ECC-SECP384R1" + }; + + for (int i = 0; i < sizeof(valid_key_types) / sizeof(valid_key_types[0]); i++) { + if (strcmp(key_type, valid_key_types[i]) == 0) { + return 1; + } + } + + return 0; +} #define DEFAULT_GLOBAL_CFG_FILENAME "/etc/certifier/libcertifier.cfg" #define DEFAULT_AUTH_TYPE "X509" #define DEFAULT_CA_INFO "libcertifier-cert.crt" @@ -53,6 +74,32 @@ #define DEFAULT_AUTORENEW_INTERVAL 86400 #define DEFAULT_AUTORENEW_CERTS_PATH "~/.libcertifier" +static char * simple_json_array_to_csv(const char *json_array_str) +{ + // Assumes input like ["a","b","c"] + if (!json_array_str || json_array_str[0] != '[') return XSTRDUP(""); + size_t len = strlen(json_array_str); + char *csv = XCALLOC(len + 1, sizeof(char)); + if (!csv) return NULL; + + size_t j = 0; + bool in_string = false; + for (size_t i = 0; i < len; ++i) { + char c = json_array_str[i]; + if (c == '"') { + in_string = !in_string; + continue; + } + if (in_string) { + csv[j++] = c; + } else if (c == ',' && j > 0) { + csv[j++] = ','; + } + } + csv[j] = '\0'; + return csv; +} + const char * get_default_cfg_filename() { static char cfg[] = DEFAULT_CFG_FILENAME; @@ -175,6 +222,21 @@ struct _PropMap X509_CERT * cert_x509_out; char * mtls_filename; char * mtls_p12_filename; + + //Sectigo properties (common properties like auth_token, validity days, source, etc. are above) + char * common_name; + char * group_name; + char * group_email; + char * id; + char * owner_first_name; + char * owner_last_name; + char * project_name; + char * business_justification; + char * subject_alt_names; + char * owner_email; + char * sectigo_url; + char * devhub_id; + char * key_type; }; static void free_prop_map_values(CertifierPropMap * prop_map); @@ -311,6 +373,70 @@ int property_set_int(CertifierPropMap * prop_map, CERTIFIER_OPT name, int value) return retval; } +int sectigo_property_set(CertifierPropMap * prop_map, int name, const void * value) +{ + int retval = 0; + switch (name) + { + case CERTIFIER_OPT_CFG_FILENAME: + prop_map->cfg_filename = XSTRDUP((const char *)value); + break; + case CERTIFIER_OPT_LOG_LEVEL: + prop_map->log_level = (int)(size_t)value; + log_set_level(prop_map->log_level); + break; + case CERTIFIER_OPT_SECTIGO_AUTH_TOKEN: + prop_map->auth_token = XSTRDUP((const char *)value); + break; + case CERTIFIER_OPT_SECTIGO_COMMON_NAME: + prop_map->common_name = XSTRDUP((const char *)value); + break; + case CERTIFIER_OPT_SECTIGO_GROUP_NAME: + prop_map->group_name = XSTRDUP((const char *)value); + break; + case CERTIFIER_OPT_SECTIGO_GROUP_EMAIL: + prop_map->group_email = XSTRDUP((const char *)value); + break; + case CERTIFIER_OPT_SECTIGO_ID: + prop_map->id = XSTRDUP((const char *)value); + break; + case CERTIFIER_OPT_SECTIGO_OWNER_FIRST_NAME: + prop_map->owner_first_name = XSTRDUP((const char *)value); + break; + case CERTIFIER_OPT_SECTIGO_OWNER_LAST_NAME: + prop_map->owner_last_name = XSTRDUP((const char *)value); + break; + case CERTIFIER_OPT_SECTIGO_PROJECT_NAME: + prop_map->project_name = XSTRDUP((const char *)value); + break; + case CERTIFIER_OPT_SECTIGO_BUSINESS_JUSTIFICATION: + prop_map->business_justification = XSTRDUP((const char *)value); + break; + case CERTIFIER_OPT_SECTIGO_SUBJECT_ALT_NAMES: + prop_map->subject_alt_names = XSTRDUP((const char *)value); + break; + case CERTIFIER_OPT_SECTIGO_OWNER_EMAIL: + prop_map->owner_email = XSTRDUP((const char *)value); + break; + case CERTIFIER_OPT_SECTIGO_URL: + prop_map->sectigo_url = XSTRDUP((const char *)value); + break; + case CERTIFIER_OPT_SECTIGO_DEVHUB_ID: + prop_map->devhub_id = XSTRDUP((const char *)value); + break; + case CERTIFIER_OPT_SECTIGO_VALIDITY_DAYS: + prop_map->validity_days = (int)(size_t)value; + break; + case CERTIFIER_OPT_SECTIGO_KEY_TYPE: + prop_map->key_type = XSTRDUP((const char *)value); + break; + default: + log_warn("sectigo_property_set: unrecognized property [%d]", name); + retval = CERTIFIER_ERR_PROPERTY_SET_10; + break; + } + return retval; +} int property_set(CertifierPropMap * prop_map, CERTIFIER_OPT name, const void * value) { int retval = 0; @@ -771,6 +897,51 @@ void * property_get(CertifierPropMap * prop_map, CERTIFIER_OPT name) break; } + case CERTIFIER_OPT_SECTIGO_AUTH_TOKEN: + retval = (void *) prop_map->auth_token; + break; + case CERTIFIER_OPT_SECTIGO_COMMON_NAME: + retval = (void *) prop_map->common_name; + break; + case CERTIFIER_OPT_SECTIGO_GROUP_NAME: + retval = (void *) prop_map->group_name; + break; + case CERTIFIER_OPT_SECTIGO_GROUP_EMAIL: + retval = (void *) prop_map->group_email; + break; + case CERTIFIER_OPT_SECTIGO_ID: + retval = (void *) prop_map->id; + break; + case CERTIFIER_OPT_SECTIGO_OWNER_FIRST_NAME: + retval = (void *) prop_map->owner_first_name; + break; + case CERTIFIER_OPT_SECTIGO_OWNER_LAST_NAME: + retval = (void *) prop_map->owner_last_name; + break; + case CERTIFIER_OPT_SECTIGO_PROJECT_NAME: + retval = (void *) prop_map->project_name; + break; + case CERTIFIER_OPT_SECTIGO_BUSINESS_JUSTIFICATION: + retval = (void *) prop_map->business_justification; + break; + case CERTIFIER_OPT_SECTIGO_SUBJECT_ALT_NAMES: + retval = (void *) prop_map->subject_alt_names; + break; + case CERTIFIER_OPT_SECTIGO_OWNER_EMAIL: + retval = (void *) prop_map->owner_email; + break; + case CERTIFIER_OPT_SECTIGO_URL: + retval = (void *) prop_map->sectigo_url; + break; + case CERTIFIER_OPT_SECTIGO_DEVHUB_ID: + retval = (void *) prop_map->devhub_id; + break; + case CERTIFIER_OPT_SECTIGO_VALIDITY_DAYS: + retval = (void *) (size_t) prop_map->validity_days; + break; + case CERTIFIER_OPT_SECTIGO_KEY_TYPE: + retval = (void *) prop_map->key_type; + break; default: log_warn("property_get: unrecognized property [%d]", name); retval = NULL; @@ -947,144 +1118,63 @@ int property_set_defaults(CertifierPropMap * prop_map) return return_code; } -int property_set_ext(CertifierPropMap * prop_map) -{ - JSON_Value * json; - const char * ext_key_usage_value = NULL; - int ret = 0; - - char * file_contents = NULL; - size_t file_contents_len = 0; - - const char * default_cfg_filename = get_default_cfg_filename(); - ret = util_slurp(default_cfg_filename, &file_contents, &file_contents_len); - if (ret != 0) - { - log_error("Received code: %i from util_slurp", ret); - if (file_contents != NULL) - { - XFREE(file_contents); - } - return 1; - } - else - { - file_contents[file_contents_len] = '\0'; - json = json_parse_string_with_comments(file_contents); - XFREE(file_contents); - if (json == NULL) - { - log_error("json_parse_string_with_comments returned a NULL value. Perhaps JSON malformed? Received error code: <%i>", - ret); - return 1; - } - } - - ext_key_usage_value = json_object_get_string(json_object(json), "libcertifier.ext.key.usage"); - if (ext_key_usage_value) - { - // log_info("Loaded Extended Key Usage Values: %s", ext_key_usage_value); - property_set(prop_map, CERTIFIER_OPT_EXT_KEY_USAGE, ext_key_usage_value); - } - - if (json) - { - json_value_free(json); - } - - return 0; -} - -int property_set_defaults_from_cfg_file(CertifierPropMap * propMap) +// Helper function to load XPKI-specific fields from JSON object +static int load_xpki_fields_from_json(CertifierPropMap *propMap, JSON_Object *root) { - - JSON_Value * json; - - const char * certifier_url_value = NULL; - const char * profile_name_value = NULL; - const char * auth_type_value = NULL; - const char * password_value = NULL; - const char * system_id_value = NULL; - const char * fabric_id_value = NULL; - const char * node_id_value = NULL; - const char * product_id_value = NULL; - const char * auth_tag_1_value = NULL; + const char *certifier_url_value = NULL; + const char *profile_name_value = NULL; + const char *auth_type_value = NULL; + const char *password_value = NULL; + const char *system_id_value = NULL; + const char *fabric_id_value = NULL; + const char *node_id_value = NULL; + const char *product_id_value = NULL; + const char *auth_tag_1_value = NULL; int http_timeout_value; int http_connect_timeout_value; int http_trace_value; - const char * input_p12_path_value = NULL; - const char * sat_token_value = NULL; - const char * ca_info_value = NULL; - const char * ca_path_value = NULL; - const char * ecc_curve_id_value = NULL; - const char * log_file_value = NULL; + const char *input_p12_path_value = NULL; + const char *sat_token_value = NULL; + const char *ca_info_value = NULL; + const char *ca_path_value = NULL; + const char *ecc_curve_id_value = NULL; + const char *log_file_value = NULL; int log_level_value; int log_max_size_value; int measure_performance_value; int autorenew_interval_value; int validity_days; - const char * source = NULL; + const char *source = NULL; int certificate_lite_value; int certificate_scopes_value; - const char * cn_prefix = NULL; - const char * ext_key_usage_value = NULL; - const char * autorenew_certs_path_list_value = NULL; - const char * mtls_p12_path_value = NULL; - const char * mtls_password_value = NULL; - - int ret = 0; - - char * file_contents = NULL; - size_t file_contents_len = 0; + const char *cn_prefix = NULL; + const char *ext_key_usage_value = NULL; + const char *autorenew_certs_path_list_value = NULL; + const char *mtls_p12_path_value = NULL; + const char *mtls_password_value = NULL; - log_info("Loading cfg file: %s", propMap->cfg_filename); - - log_debug("About to call: util_slurp with path: %s", propMap->cfg_filename); - ret = util_slurp(propMap->cfg_filename, &file_contents, &file_contents_len); - if (ret != 0) - { - log_error("Received code: %i from util_slurp", ret); - if (file_contents != NULL) - { - XFREE(file_contents); - } - return 1; - } - else - { - file_contents[file_contents_len] = '\0'; - json = json_parse_string_with_comments(file_contents); - XFREE(file_contents); - if (json == NULL) - { - log_error("json_parse_string_with_comments returned a NULL value. Perhaps JSON malformed? Received error code: <%i>", - ret); - return 1; - } - } - - certifier_url_value = json_object_get_string(json_object(json), "libcertifier.certifier.url"); + certifier_url_value = json_object_get_string(root, "libcertifier.certifier.url"); if (certifier_url_value) { log_info("Loaded certifier url: %s from config file.", certifier_url_value); property_set(propMap, CERTIFIER_OPT_CERTIFIER_URL, certifier_url_value); } - profile_name_value = json_object_get_string(json_object(json), "libcertifier.profile.name"); + profile_name_value = json_object_get_string(root, "libcertifier.profile.name"); if (profile_name_value) { log_info("Loaded profile name: %s from config file.", profile_name_value); property_set(propMap, CERTIFIER_OPT_PROFILE_NAME, profile_name_value); } - auth_type_value = json_object_get_string(json_object(json), "libcertifier.auth.type"); + auth_type_value = json_object_get_string(root, "libcertifier.auth.type"); if (auth_type_value) { log_info("Loaded crt.type: %s from config file.", auth_type_value); property_set(propMap, CERTIFIER_OPT_AUTH_TYPE, auth_type_value); } - password_value = json_object_get_string(json_object(json), "libcertifier.input.p12.password"); + password_value = json_object_get_string(root, "libcertifier.input.p12.password"); if (password_value) { print_warning("password"); @@ -1092,56 +1182,56 @@ int property_set_defaults_from_cfg_file(CertifierPropMap * propMap) property_set(propMap, CERTIFIER_OPT_INPUT_P12_PASSWORD, password_value); } - system_id_value = json_object_get_string(json_object(json), "libcertifier.system.id"); + system_id_value = json_object_get_string(root, "libcertifier.system.id"); if (system_id_value) { log_info("Loaded system_id_value: %s from config file.", system_id_value); property_set(propMap, CERTIFIER_OPT_SYSTEM_ID, system_id_value); } - fabric_id_value = json_object_get_string(json_object(json), "libcertifier.fabric.id"); + fabric_id_value = json_object_get_string(root, "libcertifier.fabric.id"); if (fabric_id_value) { log_info("Loaded fabric_id_value: %s from config file.", fabric_id_value); property_set(propMap, CERTIFIER_OPT_FABRIC_ID, fabric_id_value); } - node_id_value = json_object_get_string(json_object(json), "libcertifier.node.id"); + node_id_value = json_object_get_string(root, "libcertifier.node.id"); if (node_id_value) { log_info("Loaded node_id_value: %s from config file.", node_id_value); property_set(propMap, CERTIFIER_OPT_NODE_ID, node_id_value); } - product_id_value = json_object_get_string(json_object(json), "libcertifier.product.id"); + product_id_value = json_object_get_string(root, "libcertifier.product.id"); if (product_id_value) { log_info("Loaded product_id_value: %s from config file.", product_id_value); property_set(propMap, CERTIFIER_OPT_PRODUCT_ID, product_id_value); } - auth_tag_1_value = json_object_get_string(json_object(json), "libcertifier.authentication.tag.1"); + auth_tag_1_value = json_object_get_string(root, "libcertifier.authentication.tag.1"); if (auth_tag_1_value) { log_info("Loaded auth_tag_1_value: %s from config file.", auth_tag_1_value); property_set(propMap, CERTIFIER_OPT_AUTH_TAG_1, auth_tag_1_value); } - http_timeout_value = json_object_get_number(json_object(json), "libcertifier.http.timeout"); + http_timeout_value = json_object_get_number(root, "libcertifier.http.timeout"); if (http_timeout_value >= 0) { log_info("Loaded http_timeout_value: %i from cfg file.", http_timeout_value); property_set(propMap, CERTIFIER_OPT_HTTP_TIMEOUT, (void *) (size_t) http_timeout_value); } - http_connect_timeout_value = json_object_get_number(json_object(json), "libcertifier.http.connect.timeout"); + http_connect_timeout_value = json_object_get_number(root, "libcertifier.http.connect.timeout"); if (http_connect_timeout_value >= 0) { log_info("Loaded http_connect_timeout_value: %i from cfg file.", http_connect_timeout_value); property_set(propMap, CERTIFIER_OPT_HTTP_CONNECT_TIMEOUT, (void *) (size_t) http_connect_timeout_value); } - http_trace_value = json_object_get_number(json_object(json), "libcertifier.http.trace"); + http_trace_value = json_object_get_number(root, "libcertifier.http.trace"); if (http_trace_value == 1) { log_info("Loaded http_trace_value: %i from cfg file.", http_trace_value); @@ -1149,77 +1239,77 @@ int property_set_defaults_from_cfg_file(CertifierPropMap * propMap) propMap->options |= (CERTIFIER_OPTION_TRACE_HTTP | CERTIFIER_OPTION_DEBUG_HTTP); } - measure_performance_value = json_object_get_number(json_object(json), "libcertifier.measure.performance"); + measure_performance_value = json_object_get_number(root, "libcertifier.measure.performance"); if (measure_performance_value == 1) { log_info("Loaded measure.performance: %i from cfg file.", measure_performance_value); propMap->options |= CERTIFIER_OPTION_MEASURE_PERFORMANCE; } - autorenew_interval_value = json_object_get_number(json_object(json), "libcertifier.autorenew.interval"); + autorenew_interval_value = json_object_get_number(root, "libcertifier.autorenew.interval"); if (autorenew_interval_value == 1) { log_info("Loaded autorenew.interval: %i from cfg file.", autorenew_interval_value); property_set(propMap, CERTIFIER_OPT_AUTORENEW_INTERVAL, (void *) (size_t) autorenew_interval_value); } - input_p12_path_value = json_object_get_string(json_object(json), "libcertifier.input.p12.path"); + input_p12_path_value = json_object_get_string(root, "libcertifier.input.p12.path"); if (input_p12_path_value) { log_info("Loaded input_p12_path_value: %s from cfg file.", input_p12_path_value); property_set(propMap, CERTIFIER_OPT_INPUT_P12_PATH, input_p12_path_value); } - sat_token_value = json_object_get_string(json_object(json), "libcertifier.sat.token"); + sat_token_value = json_object_get_string(root, "libcertifier.sat.token"); if (sat_token_value) { log_info("Loaded sat_token_value: %s from cfg file.", sat_token_value); property_set(propMap, CERTIFIER_OPT_AUTH_TOKEN, sat_token_value); } - ca_info_value = json_object_get_string(json_object(json), "libcertifier.ca.info"); + ca_info_value = json_object_get_string(root, "libcertifier.ca.info"); if (ca_info_value) { log_info("Loaded ca_info_value: %s from cfg file.", ca_info_value); property_set(propMap, CERTIFIER_OPT_CA_INFO, ca_info_value); } - ca_path_value = json_object_get_string(json_object(json), "libcertifier.ca.path"); + ca_path_value = json_object_get_string(root, "libcertifier.ca.path"); if (ca_path_value) { log_info("Loaded ca_path_value: %s from cfg file.", ca_path_value); property_set(propMap, CERTIFIER_OPT_CA_PATH, ca_path_value); } - validity_days = json_object_get_number(json_object(json), "libcertifier.validity.days"); + validity_days = json_object_get_number(root, "libcertifier.validity.days"); if (validity_days) { log_info("Loaded validity_days: %d", validity_days); property_set(propMap, CERTIFIER_OPT_VALIDITY_DAYS, (void *) (size_t) validity_days); } - ecc_curve_id_value = json_object_get_string(json_object(json), "libcertifier.ecc.curve.id"); + ecc_curve_id_value = json_object_get_string(root, "libcertifier.ecc.curve.id"); if (ecc_curve_id_value) { log_info("Loaded ecc_curve_id_value: %s from cfg file.", ecc_curve_id_value); property_set(propMap, CERTIFIER_OPT_ECC_CURVE_ID, ecc_curve_id_value); } - log_file_value = json_object_get_string(json_object(json), "libcertifier.log.file"); + log_file_value = json_object_get_string(root, "libcertifier.log.file"); if (log_file_value) { log_info("Loaded Log File Value: %s from cfg file.", log_file_value); property_set(propMap, CERTIFIER_OPT_LOG_FILENAME, log_file_value); } - log_level_value = json_object_get_number(json_object(json), "libcertifier.log.level"); + log_level_value = json_object_get_number(root, "libcertifier.log.level"); if (log_level_value >= 0) { log_info("Loaded Log Level value: %i from cfg file.", log_level_value); property_set(propMap, CERTIFIER_OPT_LOG_LEVEL, (void *) (size_t) (log_level_value)); } - log_max_size_value = json_object_get_number(json_object(json), "libcertifier.log.max.size"); + log_max_size_value = json_object_get_number(root, "libcertifier.log.max.size"); if (log_max_size_value >= 0) { log_info("Loaded Log Max Size value: %i from cfg file.", log_max_size_value); @@ -1227,55 +1317,58 @@ int property_set_defaults_from_cfg_file(CertifierPropMap * propMap) } log_set_max_size(propMap->log_max_size); - source = json_object_get_string(json_object(json), "libcertifier.source.id"); + source = json_object_get_string(root, "libcertifier.source.id"); if (source) { log_info("Loaded source.id %s from cfg file.", source); property_set(propMap, CERTIFIER_OPT_SOURCE, source); } - certificate_lite_value = json_object_get_number(json_object(json), "libcertifier.certificate.lite"); + certificate_lite_value = json_object_get_number(root, "libcertifier.certificate.lite"); if (certificate_lite_value == 1) { log_info("Loaded certificate.lite: %i from cfg file.", certificate_lite_value); print_warning("certificate.lite"); propMap->options |= (CERTIFIER_OPTION_CERTIFICATE_LITE); } - certificate_scopes_value = json_object_get_number(json_object(json), "libcertifier.certificate.scopes"); + + certificate_scopes_value = json_object_get_number(root, "libcertifier.certificate.scopes"); if (certificate_scopes_value == 1) { log_info("Loaded certificate.scopes: %i from cfg file.", certificate_scopes_value); print_warning("certificate.scopes"); propMap->options |= (CERTIFIER_OPTION_USE_SCOPES); } - cn_prefix = json_object_get_string(json_object(json), "libcertifier.cn.name"); + + cn_prefix = json_object_get_string(root, "libcertifier.cn.name"); if (cn_prefix != NULL) { log_info("Loaded Common Name value: %s from cfg file.", cn_prefix); property_set(propMap, CERTIFIER_OPT_CN_PREFIX, cn_prefix); } - ext_key_usage_value = json_object_get_string(json_object(json), "libcertifier.ext.key.usage"); + + ext_key_usage_value = json_object_get_string(root, "libcertifier.ext.key.usage"); if (ext_key_usage_value) { log_info("Loaded Extended Key Usage Values: %s from cfg file.", ext_key_usage_value); property_set(propMap, CERTIFIER_OPT_EXT_KEY_USAGE, ext_key_usage_value); } - autorenew_certs_path_list_value = json_object_get_string(json_object(json), "libcertifier.autorenew.certs.path.list"); + autorenew_certs_path_list_value = json_object_get_string(root, "libcertifier.autorenew.certs.path.list"); if (autorenew_certs_path_list_value) { log_info("Loaded autorenew certs path: %s from config file.", autorenew_certs_path_list_value); property_set(propMap, CERTIFIER_OPT_AUTORENEW_CERTS_PATH_LIST, autorenew_certs_path_list_value); } - mtls_p12_path_value = json_object_get_string(json_object(json), "libcertifier.mtls.p12.path"); + mtls_p12_path_value = json_object_get_string(root, "libcertifier.mtls.p12.path"); if (mtls_p12_path_value) { log_info("Loaded mtls_p12_path_value: %s from cfg file.", mtls_p12_path_value); property_set(propMap, CERTIFIER_OPT_MTLS_P12_PATH, mtls_p12_path_value); } - mtls_password_value = json_object_get_string(json_object(json), "libcertifier.mtls.p12.password"); + mtls_password_value = json_object_get_string(root, "libcertifier.mtls.p12.password"); if (mtls_password_value) { print_warning("password"); @@ -1283,6 +1376,213 @@ int property_set_defaults_from_cfg_file(CertifierPropMap * propMap) property_set(propMap, CERTIFIER_OPT_MTLS_P12_PASSWORD, mtls_password_value); } + return 0; +} + +// Helper function to load Sectigo-specific fields from JSON object +static int load_sectigo_fields_from_json(CertifierPropMap *propMap, JSON_Object *root) +{ + // Iterate through all keys in the JSON object + size_t count = json_object_get_count(root); + for (size_t i = 0; i < count; i++) { + const char *key = json_object_get_name(root, i); + + // Only process keys that start with "libcertifier.sectigo." + if (strncmp(key, "libcertifier.sectigo.", 21) != 0) { + continue; + } + + // Special handling for array keys + if (strcmp(key, "libcertifier.sectigo.subject.alt.names") == 0) { + const char *array_str = json_object_get_string(root, key); + char *csv = NULL; + if (array_str) { + csv = simple_json_array_to_csv(array_str); + } + if (!csv) { + csv = XSTRDUP(""); // Always set to empty string if missing/empty + } + if (strlen(csv) > 0) { + log_info("Loaded sectigo subject alt names: %s from config file.", csv); + } + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_SUBJECT_ALT_NAMES, csv); + XFREE(csv); + continue; + } + + if (strcmp(key, "libcertifier.sectigo.validity.days") == 0) { + int validity_days = json_object_get_number(root, key); + log_info("Loaded sectigo validity days: %d from config file.", validity_days); + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_VALIDITY_DAYS, (void *) (size_t) validity_days); + } + + const char *value_str = json_object_get_string(root, key); + if (value_str && strlen(value_str) > 0) { // Only process non-empty values + // Map config key to property enum + if (strcmp(key, "libcertifier.sectigo.auth.token") == 0) { + log_info("Loaded sectigo auth token from config file."); + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_AUTH_TOKEN, value_str); + } + else if (strcmp(key, "libcertifier.sectigo.common.name") == 0) { + log_info("Loaded sectigo common name: %s from config file.", value_str); + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_COMMON_NAME, value_str); + } + else if (strcmp(key, "libcertifier.sectigo.group.name") == 0) { + log_info("Loaded sectigo group name: %s from config file.", value_str); + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_GROUP_NAME, value_str); + } + else if (strcmp(key, "libcertifier.sectigo.group.email") == 0) { + log_info("Loaded sectigo group email: %s from config file.", value_str); + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_GROUP_EMAIL, value_str); + } + else if (strcmp(key, "libcertifier.sectigo.id") == 0) { + log_info("Loaded sectigo id: %s from config file.", value_str); + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_ID, value_str); + } + else if (strcmp(key, "libcertifier.sectigo.owner.first.name") == 0) { + log_info("Loaded sectigo owner first name: %s from config file.", value_str); + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_OWNER_FIRST_NAME, value_str); + } + else if (strcmp(key, "libcertifier.sectigo.owner.last.name") == 0) { + log_info("Loaded sectigo owner last name: %s from config file.", value_str); + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_OWNER_LAST_NAME, value_str); + } + else if (strcmp(key, "libcertifier.sectigo.project.name") == 0) { + log_info("Loaded sectigo project name: %s from config file.", value_str); + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_PROJECT_NAME, value_str); + } + else if (strcmp(key, "libcertifier.sectigo.business.justification") == 0) { + log_info("Loaded sectigo business justification: %s from config file.", value_str); + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_BUSINESS_JUSTIFICATION, value_str); + } + else if (strcmp(key, "libcertifier.sectigo.owner.email") == 0) { + log_info("Loaded sectigo owner email: %s from config file.", value_str); + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_OWNER_EMAIL, value_str); + } + else if (strcmp(key, "libcertifier.sectigo.url") == 0) { + log_info("Loaded sectigo URL: %s from config file.", value_str); + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_URL, value_str); + } + else if (strcmp(key, "libcertifier.sectigo.devhub.id") == 0) { + log_info("Loaded sectigo devhub id: %s from config file.", value_str); + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_DEVHUB_ID, value_str); + } + else if (strcmp(key, "libcertifier.sectigo.key.type") == 0) { + if (!is_valid_sectigo_key_type(value_str)) { + log_error("Invalid key type '%s' in config file. Supported: [RSA-2048, RSA-3072, RSA-4096, RSA-8192, ECC-PRIME256V1, ECC-SECP384R1]", value_str); + exit(0); + } + log_info("Loaded sectigo key type: %s from config file.", value_str); + sectigo_property_set(propMap, CERTIFIER_OPT_SECTIGO_KEY_TYPE, value_str); + } + // Add more mappings as needed + } + } + + return 0; +} + +// Config file loader with mode-aware field loading +static int property_set_defaults_from_cfg_file_ex(CertifierPropMap *propMap, bool sectigo_mode) +{ + JSON_Value *json = NULL; + int ret = 0; + char *file_contents = NULL; + size_t file_contents_len = 0; + const char *cfg_filename = propMap->cfg_filename; + + if (!cfg_filename) { + log_warn("No config filename set, skipping defaults from cfg file"); + return 0; + } + + log_info("Loading %s cfg file: %s", sectigo_mode ? "Sectigo" : "XPKI", cfg_filename); + log_debug("property_set_defaults_from_cfg_file_ex called with sectigo_mode=%d", sectigo_mode); + + ret = util_slurp(cfg_filename, &file_contents, &file_contents_len); + if (ret != 0) { + log_error("Failed to read cfg file: %s (error code: %i)", cfg_filename, ret); + if (file_contents) { + XFREE(file_contents); + } + return 1; + } + + file_contents[file_contents_len] = '\0'; + json = json_parse_string_with_comments(file_contents); + XFREE(file_contents); + + if (!json) { + log_error("Failed to parse JSON from cfg file: %s", cfg_filename); + return 1; + } + + JSON_Object *root = json_object(json); + if (!root) { + log_error("JSON root object is NULL"); + json_value_free(json); + return 1; + } + + // Delegate to mode-specific field loader + if (sectigo_mode) { + log_debug("Delegating to load_sectigo_fields_from_json"); + ret = load_sectigo_fields_from_json(propMap, root); + } else { + log_debug("Delegating to load_xpki_fields_from_json"); + ret = load_xpki_fields_from_json(propMap, root); + } + + json_value_free(json); + return ret; +} + +int property_set_sectigo_defaults_from_cfg_file(CertifierPropMap * propMap) +{ + return property_set_defaults_from_cfg_file_ex(propMap, true); +} + + +int property_set_ext(CertifierPropMap * prop_map) +{ + JSON_Value * json; + const char * ext_key_usage_value = NULL; + int ret = 0; + + char * file_contents = NULL; + size_t file_contents_len = 0; + + const char * default_cfg_filename = get_default_cfg_filename(); + ret = util_slurp(default_cfg_filename, &file_contents, &file_contents_len); + if (ret != 0) + { + log_error("Received code: %i from util_slurp", ret); + if (file_contents != NULL) + { + XFREE(file_contents); + } + return 1; + } + else + { + file_contents[file_contents_len] = '\0'; + json = json_parse_string_with_comments(file_contents); + XFREE(file_contents); + if (json == NULL) + { + log_error("json_parse_string_with_comments returned a NULL value. Perhaps JSON malformed? Received error code: <%i>", + ret); + return 1; + } + } + + ext_key_usage_value = json_object_get_string(json_object(json), "libcertifier.ext.key.usage"); + if (ext_key_usage_value) + { + log_info("Loaded Extended Key Usage Values: %s", ext_key_usage_value); + property_set(prop_map, CERTIFIER_OPT_EXT_KEY_USAGE, ext_key_usage_value); + } + if (json) { json_value_free(json); @@ -1291,6 +1591,11 @@ int property_set_defaults_from_cfg_file(CertifierPropMap * propMap) return 0; } +int property_set_defaults_from_cfg_file(CertifierPropMap * propMap) +{ + return property_set_defaults_from_cfg_file_ex(propMap, false); +} + #define FV(field) \ if (field != NULL) \ { \ @@ -1339,4 +1644,40 @@ static void free_prop_map_values(CertifierPropMap * prop_map) security_free_cert(prop_map->cert_x509_out); FV(prop_map->mtls_filename); FV(prop_map->mtls_p12_filename); + FV(prop_map->auth_token); + FV(prop_map->common_name); + FV(prop_map->group_name); + FV(prop_map->group_email); + FV(prop_map->id); + FV(prop_map->owner_first_name); + FV(prop_map->owner_last_name); + FV(prop_map->project_name); + FV(prop_map->business_justification); + FV(prop_map->subject_alt_names); + FV(prop_map->owner_email); + FV(prop_map->sectigo_url); + FV(prop_map->devhub_id); + FV(prop_map->key_type); +} + +CertifierPropMap * property_new_sectigo(void) +{ + CertifierPropMap * prop_map = XCALLOC(1, sizeof(CertifierPropMap)); + if (prop_map == NULL) + { + log_error("Could not initialize CertifierPropMap."); + return NULL; + } + + char * trace_id = NULL; + + // generate tracking ID + trace_id = util_generate_random_value(16, ALLOWABLE_CHARACTERS); + if (trace_id) + { + property_set(prop_map, CERTIFIER_OPT_TRACKING_ID, trace_id); + XFREE(trace_id); + } + + return prop_map; } diff --git a/src/sectigo_client.c b/src/sectigo_client.c new file mode 100644 index 0000000..51903fa --- /dev/null +++ b/src/sectigo_client.c @@ -0,0 +1,384 @@ +/** + * Copyright 2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "certifier/sectigo_client.h" +#include +#include +#include "certifier/code_utils.h" +#include "certifier/types.h" +#include "certifier/certifierclient.h" +#include "certifier/certifier_internal.h" +#include "certifier/http.h" +#include "certifier/log.h" +#include "certifier/parson.h" +#include "certifier/util.h" +#include "certifier/error.h" +#include "certifier/property_internal.h" + +#include +#include +#include + +Certifier * get_sectigo_certifier_instance() +{ + static Certifier * certifier = NULL; + + if (certifier == NULL) + { + certifier = certifier_new(); + certifier->sectigo_mode = true; + certifier_set_property(certifier, CERTIFIER_OPT_LOG_LEVEL, (void *) (size_t) 0); + + // Load Sectigo config file if it exists + const char *cfg_filename = certifier_get_property(certifier, CERTIFIER_OPT_CFG_FILENAME); + if (cfg_filename && access(cfg_filename, F_OK) == 0) { + sectigo_load_cfg_file(certifier); + } + } + return certifier; +} + + +SECTIGO_CLIENT_ERROR_CODE xc_sectigo_get_default_cert_param(sectigo_get_cert_param_t * params) +{ + Certifier * certifier = get_sectigo_certifier_instance(); + + memset(params, 0, sizeof(sectigo_get_cert_param_t)); + + void * param = NULL; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_AUTH_TOKEN); + params->auth_token = param ? XSTRDUP((const char *)param) : NULL; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_COMMON_NAME); + params->common_name = param ? XSTRDUP((const char *)param) : NULL; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_GROUP_NAME); + params->group_name = param ? XSTRDUP((const char *)param) : NULL; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_GROUP_EMAIL); + params->group_email = param ? XSTRDUP((const char *)param) : NULL; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_ID); + params->id = param ? XSTRDUP((const char *)param) : NULL; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_OWNER_FIRST_NAME); + params->owner_first_name = param ? XSTRDUP((const char *)param) : NULL; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_OWNER_LAST_NAME); + params->owner_last_name = param ? XSTRDUP((const char *)param) : NULL; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_PROJECT_NAME); + params->project_name = param ? XSTRDUP((const char *)param) : NULL; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_BUSINESS_JUSTIFICATION); + params->business_justification = param ? XSTRDUP((const char *)param) : NULL; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_SUBJECT_ALT_NAMES); + params->subject_alt_names = param ? XSTRDUP((const char *)param) : NULL; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_OWNER_EMAIL); + params->owner_email = param ? XSTRDUP((const char *)param) : NULL; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_URL); + params->sectigo_url = param ? XSTRDUP((const char *)param) : NULL; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_DEVHUB_ID); + params->devhub_id = param ? XSTRDUP((const char *)param) : NULL; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_VALIDITY_DAYS); + params->validity_days = param ? (size_t) param : 365; + + param = certifier_get_property(certifier, CERTIFIER_OPT_SECTIGO_KEY_TYPE); + params->key_type = param ? XSTRDUP((const char *)param) : NULL; + + return SECTIGO_CLIENT_SUCCESS; +} + +CertifierError sectigo_client_request_certificate(CertifierPropMap * props, const unsigned char * csr, +const char * node_address, const char * certifier_id, char ** out_cert) +{ + Certifier *certifier = get_sectigo_certifier_instance(); + CertifierError rc = CERTIFIER_ERROR_INITIALIZER; + JSON_Value *root_value = NULL; + JSON_Object *root_obj = NULL; + char *json_body = NULL; + + if (out_cert == NULL) + { + rc.application_error_code = CERTIFIER_ERR_EMPTY_OR_INVALID_PARAM_1; + rc.application_error_msg = util_format_error_here("out cert cannot be null"); + return rc; + } + + char auth_header[VERY_LARGE_STRING_SIZE * 4] = ""; + char tracking_header[LARGE_STRING_SIZE] = ""; + char source_header[SMALL_STRING_SIZE] = ""; + JSON_Object * parsed_json_object_value = NULL; + JSON_Value * parsed_json_root_value = NULL; + char * serialized_string = NULL; + http_response * resp = NULL; + const char * tracking_id = property_get(props, CERTIFIER_OPT_TRACKING_ID); + const char * bearer_token = property_get(props, CERTIFIER_OPT_SECTIGO_AUTH_TOKEN); + const char * source = "libcertifier"; + const char * sectigo_base_url = property_get(props, CERTIFIER_OPT_SECTIGO_URL); + + if (!bearer_token) { + log_error("Missing CERTIFIER_OPT_SECTIGO_AUTH_TOKEN"); + rc.application_error_code = CERTIFIER_ERR_EMPTY_OR_INVALID_PARAM_1; + rc.application_error_msg = util_format_error_here("Bearer token is missing"); + goto cleanup; + } + if (!sectigo_base_url) { + log_error("Missing CERTIFIER_OPT_SECTIGO_URL"); + rc.application_error_code = CERTIFIER_ERR_EMPTY_OR_INVALID_PARAM_1; + rc.application_error_msg = util_format_error_here("Sectigo base URL is missing"); + goto cleanup; + } + + // Build full URL: base + endpoint + char sectigo_create_cert_url[256]; + char create_cert_endpoint[] = "/api/createCertificate"; + strncpy(sectigo_create_cert_url, sectigo_base_url, sizeof(sectigo_create_cert_url) - 1); + strncpy(sectigo_create_cert_url + strlen(sectigo_base_url), create_cert_endpoint, + sizeof(sectigo_create_cert_url) - 1 - strlen(sectigo_base_url)); + + log_debug("Tracking ID is: %s\n", tracking_id); + log_debug("Sectigo URL: %s\n", sectigo_create_cert_url); + + if (bearer_token != NULL) { + snprintf(auth_header, sizeof(auth_header), "Authorization: %s", bearer_token); + } + snprintf(tracking_header, sizeof(tracking_header), "x-xpki-request-id: %s", tracking_id); + snprintf(source_header, sizeof(source_header), "x-xpki-source: %s", source); + + const char *headers[] = { + "Accept: */*", + "Connection: keep-alive", + "cache-control: no-cache", + "Content-Type: application/json", + source_header, + tracking_header, + "x-xpki-partner-id: comcast", + auth_header, + NULL + }; + + CertifierError csr_rc = sectigo_generate_certificate_signing_request(certifier, &serialized_string); + + if (csr_rc.application_error_code != 0 || serialized_string == NULL) { + rc.application_error_code = csr_rc.application_error_code; + rc.application_error_msg = csr_rc.application_error_msg; + goto cleanup; + } + + // Take Mutex + if (pthread_mutex_lock(&lock) != 0) + { + rc.application_error_code = 17; + rc.application_error_msg = "sectigo_client_request_certificate: pthread_mutex_lock failed"; + goto cleanup; + } + // Give Mutex + if (pthread_mutex_unlock(&lock) != 0) + { + rc.application_error_code = 18; + rc.application_error_msg = "sectigo_client_request_certificate: pthread_mutex_unlock failed"; + goto cleanup; + } + + sectigo_get_cert_param_t params; + xc_sectigo_get_default_cert_param(¶ms); + + // Build JSON body + root_value = json_value_init_object(); + root_obj = json_value_get_object(root_value); + + json_object_set_string(root_obj, "certificateSigningRequest", serialized_string); + + json_object_set_string(root_obj, "commonName", params.common_name ? params.common_name : ""); + json_object_set_string(root_obj, "groupName", params.group_name ? params.group_name : ""); + json_object_set_string(root_obj, "groupEmailAddress", params.group_email ? params.group_email : ""); + json_object_set_string(root_obj, "id", params.id ? params.id : ""); + json_object_set_string(root_obj, "ownerFirstName", params.owner_first_name ? params.owner_first_name : ""); + json_object_set_string(root_obj, "ownerLastName", params.owner_last_name ? params.owner_last_name : ""); + json_object_set_string(root_obj, "projectName", params.project_name ? params.project_name : ""); + json_object_set_string(root_obj, "businessJustification", params.business_justification ? params.business_justification : ""); + json_object_set_string(root_obj, "certificateType", "comodo"); // Always "comodo" + json_object_set_string(root_obj, "ownerEmailAddress", params.owner_email ? params.owner_email : ""); + + // The following parameters are optional. Only include if set + if (params.devhub_id) { + json_object_set_string(root_obj, "devhubId", params.devhub_id); + } + + if (params.validity_days > 0) { + json_object_set_number(root_obj, "validityDays", (double)params.validity_days); + } + + if (params.key_type) { + json_object_set_string(root_obj, "keyType", params.key_type); + } + + // subjectAltNames as array + JSON_Value *san_array = json_value_init_array(); + JSON_Array *san_json_array = json_value_get_array(san_array); + if (params.subject_alt_names && strlen(params.subject_alt_names) > 0) { + char *san_copy = XSTRDUP(params.subject_alt_names); + char *token = strtok(san_copy, ","); + while (token) { + json_array_append_value(san_json_array, json_value_init_string(token)); + token = strtok(NULL, ","); + } + XFREE(san_copy); + } + json_object_set_value(root_obj, "subjectAltNames", san_array); + + json_body = json_serialize_to_string(root_value); + + resp = http_post(props, sectigo_create_cert_url, headers, json_body); + if (resp == NULL) + { + goto cleanup; + } + + rc.application_error_code = resp->error; + + // Check for errors + if (resp->error != 0) + { + rc.application_error_msg = util_format_curl_error("sectigo_client_request_certificate", resp->http_code, resp->error, + resp->error_msg, resp->payload, __FILE__, __LINE__); + goto cleanup; + } + + if (resp->payload == NULL) + { + log_error("ERROR: Failed to populate payload"); + goto cleanup; + } + + parsed_json_root_value = json_parse_string_with_comments(resp->payload); + if (json_value_get_type(parsed_json_root_value) != JSONObject) + { + rc.application_error_msg = + util_format_curl_error("sectigo_client_request_certificate", resp->http_code, resp->error, + "Could not parse JSON. Expected it to be an array.", resp->payload, __FILE__, __LINE__); + goto cleanup; + } + + parsed_json_object_value = json_value_get_object(parsed_json_root_value); + + if (parsed_json_object_value == NULL) + { + rc.application_error_msg = + util_format_curl_error("sectigo_client_request_certificate", resp->http_code, resp->error, + "Could not parse JSON. parsed_json_object_value is NULL!.", resp->payload, __FILE__, __LINE__); + goto cleanup; + } + +cleanup: + http_free_response(resp); + + if (parsed_json_root_value) json_value_free(parsed_json_root_value); + + XFREE(serialized_string); + + if (json_body) json_free_serialized_string(json_body); + if (root_value) json_value_free(root_value); + + return rc; +} + +SECTIGO_CLIENT_ERROR_CODE xc_sectigo_get_cert(sectigo_get_cert_param_t * params) +{ + Certifier *certifier = get_sectigo_certifier_instance(); + + // Build JSON body + JSON_Value *root_value = json_value_init_object(); + JSON_Object *root_obj = json_value_get_object(root_value); + + // Add all parameters to JSON body using passed-in params + if (params->common_name) + json_object_set_string(root_obj, "commonName", params->common_name); + if (params->group_name) + json_object_set_string(root_obj, "groupName", params->group_name); + if (params->group_email) + json_object_set_string(root_obj, "groupEmailAddress", params->group_email); + if (params->id) + json_object_set_string(root_obj, "id", params->id); + if (params->owner_first_name) + json_object_set_string(root_obj, "ownerFirstName", params->owner_first_name); + if (params->owner_last_name) + json_object_set_string(root_obj, "ownerLastName", params->owner_last_name); + if (params->project_name) + json_object_set_string(root_obj, "projectName", params->project_name); + if (params->business_justification) + json_object_set_string(root_obj, "businessJustification", params->business_justification); + + // subjectAltNames as array + JSON_Value *san_array = json_value_init_array(); + JSON_Array *san_json_array = json_value_get_array(san_array); + if (params->subject_alt_names && strlen(params->subject_alt_names) > 0) { + char *san_copy = XSTRDUP(params->subject_alt_names); + char *token = strtok(san_copy, ","); + while (token) { + json_array_append_value(san_json_array, json_value_init_string(token)); + token = strtok(NULL, ","); + } + XFREE(san_copy); + } + json_object_set_value(root_obj, "subjectAltNames", san_array); + + json_object_set_string(root_obj, "certificateType", "comodo"); // Always "comodo" + + if (params->owner_email) + json_object_set_string(root_obj, "ownerEmailAddress", params->owner_email); + + // Generate CSR and add to body + char *csr_pem = NULL; + CertifierError csr_rc = sectigo_generate_certificate_signing_request(certifier, &csr_pem); + if (csr_rc.application_error_code != 0 || csr_pem == NULL) { + log_error("Failed to generate CSR: %s\n", csr_rc.application_error_msg); + if (csr_pem) XFREE(csr_pem); + json_value_free(root_value); + return csr_rc.application_error_code; + } + json_object_set_string(root_obj, "certificateSigningRequest", csr_pem); + + // Serialize JSON body + char *json_body = json_serialize_to_string(root_value); + + // Call the request function + CertifierPropMap *props = certifier_get_prop_map(certifier); + char *cert_output = NULL; + CertifierError req_rc = sectigo_client_request_certificate( + props, + (unsigned char *)csr_pem, + NULL, // node_address + NULL, // certifier_id + &cert_output + ); + + // Cleanup + if (csr_pem) XFREE(csr_pem); + if (json_body) json_free_serialized_string(json_body); + if (root_value) json_value_free(root_value); + + return req_rc.application_error_code; +} diff --git a/src/xpki_client.c b/src/xpki_client.c index ac233e5..978eab0 100644 --- a/src/xpki_client.c +++ b/src/xpki_client.c @@ -51,6 +51,8 @@ XPKI_AUTH_TYPE map_to_xpki_auth_type(const char * str) { return XPKI_AUTH_SAT; } + + return XPKI_AUTH_X509; // Default to X509 } const char * xpki_auth_type_to_string(XPKI_AUTH_TYPE auth_type) diff --git a/tests/tests.c b/tests/tests.c index 5e86f55..698430b 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -107,7 +107,8 @@ static void mock_http_set_response_success(const char * body, int status) g_mock_http_response.error = 0; } -static void mock_http_set_response_failure(const char * err, int status) +// Add unused attribute marker to suppress warning +__attribute__((unused)) static void mock_http_set_response_failure(const char * err, int status) { g_mock_http_response.payload = NULL; g_mock_http_response.http_code = status; @@ -115,7 +116,7 @@ static void mock_http_set_response_failure(const char * err, int status) g_mock_http_response.error = status; } -static void test_certifier_client_requests(void ** state) +static void test_certifier_client_requests(void) { const char * csr = "CSr"; @@ -163,9 +164,9 @@ static void test_certifier_client_requests(void ** state) certifier_set_property(certifier, CERTIFIER_OPT_PROFILE_NAME, profile_name); certifier_set_property(certifier, CERTIFIER_OPT_PRODUCT_ID, product_id); - int options = certifier_get_property(certifier, CERTIFIER_OPT_OPTIONS); - options |= CERTIFIER_OPT_CERTIFICATE_LITE; - certifier_set_property(certifier, CERTIFIER_OPT_OPTIONS, options); + int options = (int)(size_t)certifier_get_property(certifier, CERTIFIER_OPT_OPTIONS); + options |= CERTIFIER_OPTION_CERTIFICATE_LITE; + certifier_set_property(certifier, CERTIFIER_OPT_OPTIONS, (void *)(size_t)options); CertifierError rc = certifierclient_request_x509_certificate(_certifier_get_properties(certifier), (unsigned char *) csr, node_address, certifier_id, &ret); @@ -181,7 +182,7 @@ static void test_certifier_client_requests(void ** state) } } -static void test_certifier_client_requests1(void ** state) +static void test_certifier_client_requests1(void) { const char * csr = "CSr"; @@ -196,7 +197,7 @@ static void test_certifier_client_requests1(void ** state) const char * source = "test_libledger"; char * ret = NULL; char * cn_prefix = NULL; - int icount = 0, return_code = 0; + int return_code = 0; unsigned int num_days = 0; JSON_Value * root_value = json_value_init_object(); @@ -207,11 +208,11 @@ static void test_certifier_client_requests1(void ** state) return_code = certifier_set_property(certifier, CERTIFIER_OPT_CN_PREFIX, "xcal.tv"); assert_int_equal(0, return_code); - return_code = certifier_set_property(certifier, CERTIFIER_OPT_VALIDITY_DAYS, 730); + return_code = certifier_set_property(certifier, CERTIFIER_OPT_VALIDITY_DAYS, (void *)(size_t)730); assert_int_equal(0, return_code); cn_prefix = certifier_get_property(certifier, CERTIFIER_OPT_CN_PREFIX); - num_days = certifier_get_property(certifier, CERTIFIER_OPT_VALIDITY_DAYS); + num_days = (unsigned int)(size_t)certifier_get_property(certifier, CERTIFIER_OPT_VALIDITY_DAYS); assert_int_equal(730, num_days); if (cn_prefix) { @@ -253,9 +254,9 @@ static void test_certifier_client_requests1(void ** state) certifier_set_property(certifier, CERTIFIER_OPT_PROFILE_NAME, profile_name); certifier_set_property(certifier, CERTIFIER_OPT_PRODUCT_ID, product_id); - int options = certifier_get_property(certifier, CERTIFIER_OPT_OPTIONS); - options |= CERTIFIER_OPT_CERTIFICATE_LITE; - certifier_set_property(certifier, CERTIFIER_OPT_OPTIONS, options); + int options = (int)(size_t)certifier_get_property(certifier, CERTIFIER_OPT_OPTIONS); + options |= CERTIFIER_OPTION_CERTIFICATE_LITE; + certifier_set_property(certifier, CERTIFIER_OPT_OPTIONS, (void *)(size_t)options); CertifierError rc = certifierclient_request_x509_certificate(_certifier_get_properties(certifier), (unsigned char *) csr, cn_prefix, ledger_id, &ret); @@ -275,7 +276,7 @@ static void test_certifier_client_requests1(void ** state) } } -static void test_certifier_create_crt1(void ** state) +static void test_certifier_create_crt1(void) { int rc = 0; char * out_crt = NULL; @@ -294,7 +295,7 @@ static void test_certifier_create_crt1(void ** state) XFREE(out_crt); } -static void test_certifier_create_node_address(void ** state) +static void test_certifier_create_node_address(void) { int rc = 0; char * node_address = NULL; @@ -310,7 +311,7 @@ static void test_certifier_create_node_address(void ** state) XFREE(node_address); } -static void test_certifier_get_version(void ** state) +static void test_certifier_get_version(void) { char * out_version = certifier_get_version(certifier); @@ -373,7 +374,7 @@ static int tearDown(void ** state) #endif -static void test_base64(void ** state) +static void test_base64(void) { char buf[64]; @@ -415,7 +416,7 @@ static void test_base64(void ** state) assert_memory_equal("\x01\x02\xFF\x80\x81\x7F\x42\x42", buf, 8); } -static void test_base58(void ** state) +static void test_base58(void) { char b58[128]; uint8_t input[26]; @@ -449,7 +450,7 @@ static void test_base58(void ** state) assert_string_equal("1112", b58); } -static void test_file_utils(void ** state) +static void test_file_utils(void) { char temp_file[128]; XSTRCPY(temp_file, "/tmp/certifier_test.XXXXXXX"); @@ -472,7 +473,7 @@ static void test_file_utils(void ** state) assert_int_equal(-1, util_delete_file("/tmp/moved_temp")); } -void test_sha256_ripemd_b58(void ** state) +void test_sha256_ripemd_b58(void) { #define NUM_HASH_INPUTS 180 @@ -556,7 +557,7 @@ void test_sha256_ripemd_b58(void ** state) } } -static void test_set_curl_error(void ** state) +static void test_set_curl_error(void) { char errbuf[1024] = { "some error" }; int res = 0; @@ -566,7 +567,7 @@ static void test_set_curl_error(void ** state) XFREE(err); } -static void test_str_utils(void ** state) +static void test_str_utils(void) { char str[128] = { 0 }; @@ -629,7 +630,7 @@ static void test_str_utils(void ** state) assert_true(util_is_empty(line4)); } -static void test_random_val(void ** state) +static void test_random_val(void) { char * str = util_generate_random_value(5, ""); @@ -661,7 +662,7 @@ static void test_random_val(void ** state) assert_true(b_cnt > 32); } -static void test_ecc_key(void ** state) +static void test_ecc_key(void) { // verify free(NULL) is ok @@ -741,7 +742,7 @@ static void test_ecc_key(void ** state) XFREE(csr); } -static void test_verify_signature_1(void ** state) +static void test_verify_signature_1(void) { const char * pub_key_b64 = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPGYYPEBOW/v/Kori+9rkwyDLijQ+OyOcXWN/" @@ -764,7 +765,7 @@ static void test_verify_signature_1(void ** state) security_free_eckey(deserialized_ecc_key); } -static void test_verify_signature_2(void ** state) +static void test_verify_signature_2(void) { int i = 0; @@ -871,7 +872,7 @@ static void test_verify_signature_2(void ** state) } } -void test_x509_cert(void ** state) +void test_x509_cert(void) { const char * digicert_pem_pkcs7_blob = "-----BEGIN PKCS7-----\n" @@ -1127,7 +1128,7 @@ void test_x509_cert(void ** state) security_free_cert_list(cert_list); } -static void test_pkcs12(void ** state) +static void test_pkcs12(void) { const char * pkcs12_blob_base64 = "MIII6AIBAzCCCK4GCSqGSIb3DQEHAaCCCJ8EggibMIIIlzCCB2oGCSqGSIb3DQEHAaCCB1sEggdXMIIHUzCCAz8GCyqGSIb3DQEMCgEDoIIC8DCCAuwGCiqGSI" @@ -1172,7 +1173,6 @@ static void test_pkcs12(void ** state) XFILE pkcs12_file = NULL; X509_LIST * certs = NULL; ECC_KEY * key = NULL; - ECC_KEY * dup_key = NULL; X509_CERT * cert = NULL; char * certifier_id = NULL; char * generated_crt = NULL; @@ -1181,12 +1181,8 @@ static void test_pkcs12(void ** state) unsigned char * der_key = NULL; int der_key_len = 0; int ret = 0; - - char * tmp_crt = NULL; - int rc = 0; - const char * expires = "0"; - const char * action = "allow"; + certifier_set_property(certifier, CERTIFIER_OPT_OUTPUT_NODE, "dummy output node"); blob_len = base64_decode(pkcs12_blob, pkcs12_blob_base64); @@ -1387,7 +1383,7 @@ static void test_pkcs12(void ** state) * The Java equivalent test took ~ 5189 ms to complete 1 million iterations. * This took ~ 4852 ms seconds in C (using -Os compiler flag). */ -static void test_sha256_ripemd_b58_performance(void ** state) +static void test_sha256_ripemd_b58_performance(void) { const char * value_prefix = "value12345678890000000000000000000000-0"; @@ -1438,7 +1434,7 @@ static void cleanup_logs(void) } // still a WIP - need more testing -static void test_logging(void ** state) +static void test_logging(void) { int i; @@ -1464,7 +1460,7 @@ static void test_logging(void ** state) cleanup_logs(); } -static void test_options(void ** state) +static void test_options(void) { CertifierPropMap * props = _certifier_get_properties(certifier); diff --git a/tests/xc_apis/xc_api_tests.c b/tests/xc_apis/xc_api_tests.c index 183c30e..738c36e 100644 --- a/tests/xc_apis/xc_api_tests.c +++ b/tests/xc_apis/xc_api_tests.c @@ -18,7 +18,7 @@ #include #include - +#include #include static const char * token = NULL; @@ -237,7 +237,33 @@ static void test_get_cert_validity() TEST_ASSERT_EQUAL_INT(XPKI_CLIENT_SUCCESS, error); TEST_ASSERT_EQUAL_INT(XPKI_CLIENT_CERT_ABOUT_TO_EXPIRE, status); } - +static void test_get_sectigo_cert() +{ + SECTIGO_CLIENT_ERROR_CODE error; + sectigo_get_cert_param_t params = { 0 }; + + // Fill parameters (simulate config or CLI) + params.auth_token = "token"; + params.common_name = "sectigotest.comcast.com"; + params.group_name = "GroupName"; + params.group_email = "example@comcast.com"; + params.id = "exid"; + params.owner_first_name = "First"; + params.owner_last_name = "Last"; + params.project_name = "Testing create with SAT"; + params.business_justification = "Testing create with SAT"; + params.subject_alt_names = "*"; + params.owner_email = "first_last@comcast.com"; + params.sectigo_url = "https://certs-dev.xpki.io/api/createCertificate"; + params.devhub_id = "12345"; + params.validity_days = 90; + params.key_type = "RSA-8192"; + + // Call the API + error = xc_sectigo_get_cert(¶ms); + + TEST_ASSERT_EQUAL_INT(SECTIGO_CLIENT_SUCCESS, error); +} int main(int argc, char ** argv) { UNITY_BEGIN(); @@ -254,6 +280,7 @@ int main(int argc, char ** argv) RUN_TEST(test_renew_cert); RUN_TEST(test_print_cert_validity); RUN_TEST(test_get_cert_validity); + RUN_TEST(test_get_sectigo_cert); return UNITY_END(); }