From 473a009905b43f39d8ce609730861e7717e2e5fa Mon Sep 17 00:00:00 2001 From: alashkova Date: Thu, 10 Oct 2024 12:17:33 +0300 Subject: [PATCH 01/37] =?UTF-8?q?SUPPORT-8592.=20=D0=9D=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=B9=D0=BA=D0=B0=20cp=5Ffile=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=D0=B0=20=D0=B8=D0=B7=20[si?= =?UTF-8?q?gn]=20=D0=B2=20[main]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- conf/ervu-sign-module.conf.example | 2 +- src/main_conf.c | 14 ++++++++++++-- src/main_conf.h | 1 + src/modules/service_sign.c | 15 --------------- src/modules/service_sign.h | 2 -- src/service_manager.c | 7 +++++++ 7 files changed, 24 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 096a6b7..c39f2d9 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,9 @@ cmake -DCONFIG_NAME=/opt/ervu-sign-module.conf .. Приложение настраивается в конфигурационном файле, заданном на этапе сборки (по умолчанию, /etc/ervu-sign-module.conf). -- В секции **\[main\]** задать количество воркеров -worker_processes = 10 *\# значение по умолчанию: 10* +- В секции **\[main\]** задать общие настройки: +worker_processes = 10 *\# количество воркеров (значение по умолчанию: 10)* +cp_file = libcapi20.so *\# путь до файла библиотеки криптопровайдера* - В секции **\[fcgi\]** задать настройки fcgi-сервера: fcgi_listen_port = 9009 *\# значение по умолчанию: 9009, должно совпадать со значением в nginx.conf* @@ -56,7 +57,6 @@ fcgi_thread_pool_size = 1 *\# значение по умолчанию: 1* - В секции **\[sign\]** задать настройки модуля подписания: location = /sign *\# значение по умолчанию: /sign, должно совпадать со значением в nginx.conf* -cp_file = libcapi20.so *\# путь до файла библиотеки криптопровайдера* signer_subject = signer@example.ru *\# email, ИНН, СНИЛС или любая другая строка из свойства контейнера «Субъект»* pin = \*\*\*\* *\# пароль от контейнера* diff --git a/conf/ervu-sign-module.conf.example b/conf/ervu-sign-module.conf.example index 76dfb54..b6b307b 100644 --- a/conf/ervu-sign-module.conf.example +++ b/conf/ervu-sign-module.conf.example @@ -1,5 +1,6 @@ [main] #worker_processes = 10 +cp_file = /opt/cprocsp/lib/amd64/libcapi20.so [fcgi] fcgi_listen_port = 9009 @@ -8,6 +9,5 @@ fcgi_listen_host = 127.0.0.1 [sign] #location = /sign -cp_file = /opt/cprocsp/lib/amd64/libcapi20.so signer_subject = signer@example.ru pin = **** diff --git a/src/main_conf.c b/src/main_conf.c index 6bfcd56..feeb0c4 100644 --- a/src/main_conf.c +++ b/src/main_conf.c @@ -3,8 +3,9 @@ #include #include -#define MAIN_CONF_SECTION "main" -#define MAIN_CONF_KEY_WORKER_PROCESSES "worker_processes" +#define MAIN_CONF_SECTION "main" +#define MAIN_CONF_KEY_WORKER_PROCESSES "worker_processes" +#define MAIN_CONF_KEY_CP_FILE "cp_file" /* default configuration values: */ static const int FCGI_CONF_DEFAULT_WORKER_PROCESSES = 10; @@ -40,6 +41,14 @@ main_conf_load(main_conf_t* conf, const char *filename, const conf_file_context_ CONF_FILE_VALUE_POSITIVE_INT, &FCGI_CONF_DEFAULT_WORKER_PROCESSES }, + { + MAIN_CONF_SECTION, + MAIN_CONF_KEY_CP_FILE, + &(conf->cp_file), + CONF_FILE_VALUE_STRING, + CONF_FILE_VALUE_NONE, + NULL + }, }; if (conf_file_load_values(conf_file, fields, sizeof(fields) / sizeof(conf_file_field_t))) { @@ -70,6 +79,7 @@ main_conf_clear(main_conf_t* conf) return; } + free(conf->cp_file); free(conf->conf_file); memset(conf, 0, sizeof(main_conf_t)); } diff --git a/src/main_conf.h b/src/main_conf.h index 2e32634..c0af5d7 100644 --- a/src/main_conf.h +++ b/src/main_conf.h @@ -5,6 +5,7 @@ typedef struct main_conf_s { int worker_processes; + char *cp_file; /* файл криптопровайдера */ char *conf_file; } main_conf_t; diff --git a/src/modules/service_sign.c b/src/modules/service_sign.c index e368834..b4c4fe1 100644 --- a/src/modules/service_sign.c +++ b/src/modules/service_sign.c @@ -6,7 +6,6 @@ #define SIGN_CONF_SECTION "sign" #define SIGN_CONF_KEY_LOCATION "location" -#define SIGN_CONF_KEY_CP_FILE "cp_file" #define SIGN_CONF_KEY_SIGNER "signer_subject" #define SIGN_CONF_KEY_PIN "pin" @@ -64,14 +63,6 @@ sign_conf_load(sign_conf_t *conf, const conf_file_context_t conf_file) CONF_FILE_VALUE_LOCATION, &SIGN_CONF_DEFAULT_LOCATION }, - { - SIGN_CONF_SECTION, - SIGN_CONF_KEY_CP_FILE, - &(conf->cp_file), - CONF_FILE_VALUE_STRING, - CONF_FILE_VALUE_NONE, - NULL - }, { SIGN_CONF_SECTION, SIGN_CONF_KEY_SIGNER, @@ -116,7 +107,6 @@ sign_conf_clear(sign_conf_t *conf) str_t_clear(&conf->pin); str_t_clear(&conf->location); - free(conf->cp_file); free(conf->signer); memset(conf, 0, sizeof(sign_conf_t)); @@ -135,11 +125,6 @@ sign_service_create(const sign_conf_t *conf) hsign->conf = conf; - if (!cryptopro_init(conf->cp_file)) { - LOG_ERROR("Could not init Cryptographic Provider"); - goto error; - } - cryptopro_context_set(&hsign->cryptopro_ctx, conf->signer, &conf->pin); LOG_TRACE("sign_service_create exit"); diff --git a/src/modules/service_sign.h b/src/modules/service_sign.h index 9f999d0..8289199 100644 --- a/src/modules/service_sign.h +++ b/src/modules/service_sign.h @@ -11,8 +11,6 @@ typedef struct sign_service_t* HSign; typedef struct sign_conf_s { str_t location; - char *cp_file; /* файл криптопровайдера */ - char *signer; /* субъект контейнера */ str_t pin; /* pin-код от контейнера */ diff --git a/src/service_manager.c b/src/service_manager.c index 5de04de..4d8a523 100644 --- a/src/service_manager.c +++ b/src/service_manager.c @@ -1,5 +1,7 @@ #include "service_manager.h" +#include "utils/cryptopro.h" + #include @@ -217,6 +219,11 @@ init_services(service_manager_t* services, const service_manager_conf_t* service goto error; } + if (!cryptopro_init(services_cf->main_cf.cp_file)) { + LOG_ERROR("Could not init Cryptographic Provider"); + goto error; + } + services->hsign = sign_service_create(&services_cf->sign_cf); if (services->hsign == NULL) { goto error; From bf197c89cd30c19185b5fcfe93881c32c3d4fa1c Mon Sep 17 00:00:00 2001 From: alashkova Date: Fri, 11 Oct 2024 13:17:47 +0300 Subject: [PATCH 02/37] =?UTF-8?q?SUPPORT-8592.=20=D0=A0=D0=B5=D1=84=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3.=20=D0=92=D1=8B?= =?UTF-8?q?=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=D1=8B=20=D0=B8=D0=B7=20service?= =?UTF-8?q?=5Fsign=20=D0=B2=20fcgi=5Futils:=20check=5Fcontent=5Flength(),?= =?UTF-8?q?=20check=5Fcontent=5Ftype()=20=D0=B8=20fcgi=5Frequest=5Fload=5F?= =?UTF-8?q?data()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/fcgisrv/fcgi_utils.c | 88 ++++++++++++++++++++++++++++++++++ src/fcgisrv/fcgi_utils.h | 30 ++++++++++++ src/modules/service_sign.c | 97 ++------------------------------------ 3 files changed, 121 insertions(+), 94 deletions(-) diff --git a/src/fcgisrv/fcgi_utils.c b/src/fcgisrv/fcgi_utils.c index ae25c28..5c60f6d 100644 --- a/src/fcgisrv/fcgi_utils.c +++ b/src/fcgisrv/fcgi_utils.c @@ -17,6 +17,60 @@ fcgi_get_content_length(const FCGX_Request* request) } +fcgi_handler_status_t +check_content_length(int content_length, int client_max_body_size) +{ + LOG_TRACE("check_content_length"); + + if (content_length > 0 && content_length <= client_max_body_size) { + LOG_DEBUG("Content-Length is ok"); + return HANDLER_SUCCESS; + } + + if (content_length == 0) { + LOG_DEBUG("Content-Length == 0, so there is no body"); + return HANDLER_HTTP_OK; + } + + if (content_length < 0) { + LOG_WARN("Content-Length < 0 (%d)", content_length); + return HANDLER_HTTP_BAD_REQUEST; + } + + if (content_length > client_max_body_size) { + LOG_WARN("Content-Length (%d) is too large (> %d)", + content_length, + client_max_body_size); + return HANDLER_HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + return HANDLER_ERROR; +} + + +fcgi_handler_status_t +check_content_type(const FCGX_Request* request, const char* acceptable_content_type) +{ + LOG_TRACE("check_content_type"); + + const char* content_type = FCGX_GetParam(FCGI_PARAM_NAME_CONTENT_TYPE, request->envp); + if (content_type == NULL) { + LOG_ERROR("Could not get FCGI param: "FCGI_PARAM_NAME_CONTENT_TYPE); + return HANDLER_HTTP_BAD_REQUEST; + } + + if (strcmp(content_type, acceptable_content_type) == 0) { + LOG_DEBUG("Content-Type is ok"); + return HANDLER_SUCCESS; + } + + LOG_WARN("Content-Type is not acceptable, '%s' (!= '%s')", + content_type, acceptable_content_type); + + return HANDLER_HTTP_NOT_ACCEPTABLE; +} + + char* fcgi_request_get_param(const FCGX_Request* request, const char* name, /*out*/ bool* err) { @@ -45,6 +99,40 @@ error: } +fcgi_handler_status_t +fcgi_request_load_data(const FCGX_Request* request, int content_length, char** content) +{ + LOG_TRACE("fcgi_request_load_data enter"); + + char* data = malloc(content_length + 1); + if (data == NULL) { + LOG_ERROR("Could not allocate memory for request body (%d bytes)", content_length + 1); + return HANDLER_ERROR; + } + + int actual_content_length = FCGX_GetStr(data, content_length, request->in); + + if (actual_content_length != content_length) { + LOG_ERROR("fcgi_request_load_data: " + "actual_content_length(%d) != content_length(%d); content: '%.*s'", + actual_content_length, content_length, + actual_content_length, data); + free(data); + return HANDLER_HTTP_BAD_REQUEST; + } + + data[content_length] = '\0'; + *content = data; + + LOG_DEBUG("content_length: %d", content_length); + LOG_DEBUG("content: '%s'", *content); + + LOG_TRACE("fcgi_request_load_data exit"); + + return HANDLER_SUCCESS; +} + + fcgi_handler_status_t fcgi_printf_str(const FCGX_Request* request, const char* response, int response_len) { diff --git a/src/fcgisrv/fcgi_utils.h b/src/fcgisrv/fcgi_utils.h index 2202237..bd3197c 100644 --- a/src/fcgisrv/fcgi_utils.h +++ b/src/fcgisrv/fcgi_utils.h @@ -56,6 +56,25 @@ char* fcgi_request_get_param(const FCGX_Request* request, const char* name, bool int fcgi_get_content_length(const FCGX_Request* request); +/** + * Returns: HANDLER_SUCCESS, HANDLER_HTTP_OK (no body), HANDLER_HTTP_BAD_REQUEST, + * HANDLER_HTTP_REQUEST_ENTITY_TOO_LARGE, HANDLER_ERROR +*/ +fcgi_handler_status_t +check_content_length(int content_length, int client_max_body_size); + +/** + * Returns: HANDLER_SUCCESS, HANDLER_HTTP_BAD_REQUEST, HANDLER_HTTP_NOT_ACCEPTABLE +*/ +fcgi_handler_status_t +check_content_type(const FCGX_Request* request, const char* acceptable_content_type); + +/** + * Returns: HANDLER_SUCCESS, HANDLER_HTTP_BAD_REQUEST, HANDLER_ERROR +*/ +fcgi_handler_status_t +fcgi_request_load_data(const FCGX_Request* request, int content_length, char** content); + fcgi_handler_status_t fcgi_printf_str(const FCGX_Request* request, const char* response, int response_len); @@ -70,6 +89,10 @@ fcgi_printf_header(const FCGX_Request* request, const char* name, const char* va #define CRLF "\r\n" +#define FCGI_200_RESPONSE_FORMAT \ + "Status: 200 OK" CRLF\ + CRLF + #define FCGI_400_RESPONSE_FORMAT \ "Status: 400 Bad Request" CRLF\ "Content-type: text/plain" CRLF\ @@ -123,6 +146,13 @@ typedef fcgi_handler_status_t (*fcgi_request_handler_pt)(const FCGX_Request* req typedef fcgi_request_handler_pt (*FCGI_REQUEST_FINALIZE_HANDLER)(int rc); +static inline fcgi_handler_status_t +fcgi_200_ok_handler(const FCGX_Request* request, void* ctx __attribute__((unused))) +{ + LOG_INFO("response status: '200 OK'"); + return FCGI_CHECK_PRINTF_STATUS(request, FCGI_200_RESPONSE_FORMAT, 200); +} + static inline fcgi_handler_status_t fcgi_400_bad_request_handler(const FCGX_Request* request, void* ctx __attribute__((unused))) { diff --git a/src/modules/service_sign.c b/src/modules/service_sign.c index b4c4fe1..429c142 100644 --- a/src/modules/service_sign.c +++ b/src/modules/service_sign.c @@ -17,7 +17,7 @@ static const str_t SIGN_CONF_DEFAULT_LOCATION = str_t_const("/sign"); static const int CLIENT_MAX_BODY_SIZE = 4096; -static const str_t ACCEPTABLE_CONTENT_TYPE = str_t_const("text/plain"); +static const char* ACCEPTABLE_CONTENT_TYPE = "text/plain"; typedef struct sign_service_s { const sign_conf_t *conf; @@ -38,13 +38,6 @@ static void fcgi_sign_request_clear(fcgi_sign_request_t *req_info); static fcgi_request_handler_pt fcgi_request_finalize_handler(fcgi_handler_status_t status); -static fcgi_handler_status_t check_content_length(int content_length); - -static fcgi_handler_status_t check_content_type(const FCGX_Request* request); - -static fcgi_handler_status_t fcgi_request_load_data(const FCGX_Request* request, - int content_length, char** content); - static int sign_content(const sign_service_t *hsign, fcgi_sign_request_t *req_info); int @@ -162,12 +155,12 @@ fcgi_sign_handler(FCGX_Request* request, void* ctx) req_info.content_length = fcgi_get_content_length(request); - status = check_content_length(req_info.content_length); + status = check_content_length(req_info.content_length, CLIENT_MAX_BODY_SIZE); if (status != HANDLER_SUCCESS) { goto exit; } - status = check_content_type(request); + status = check_content_type(request, ACCEPTABLE_CONTENT_TYPE); if (status != HANDLER_SUCCESS) { goto exit; } @@ -253,90 +246,6 @@ fcgi_request_finalize_handler(fcgi_handler_status_t status) return handler; } -static fcgi_handler_status_t -check_content_length(int content_length) -{ - LOG_TRACE("check_content_length"); - - if (content_length > 0 && content_length <= CLIENT_MAX_BODY_SIZE) { - LOG_DEBUG("Content-Length is ok"); - return HANDLER_SUCCESS; - } - - if (content_length == 0) { - LOG_DEBUG("Content-Length == 0, so there is nothing to sign"); - return HANDLER_HTTP_OK; - } - - if (content_length < 0) { - LOG_WARN("Content-Length < 0 (%d)", content_length); - return HANDLER_HTTP_BAD_REQUEST; - } - - if (content_length > CLIENT_MAX_BODY_SIZE) { - LOG_WARN("Content-Length (%d) is too large (> %d)", - content_length, - CLIENT_MAX_BODY_SIZE); - return HANDLER_HTTP_REQUEST_ENTITY_TOO_LARGE; - } - - return HANDLER_ERROR; -} - -static fcgi_handler_status_t -check_content_type(const FCGX_Request* request) -{ - LOG_TRACE("check_content_type"); - - const char* content_type = FCGX_GetParam(FCGI_PARAM_NAME_CONTENT_TYPE, request->envp); - if (content_type == NULL) { - LOG_ERROR("Could not get FCGI param: "FCGI_PARAM_NAME_CONTENT_TYPE); - return HANDLER_HTTP_BAD_REQUEST; - } - - if (strncmp(content_type, ACCEPTABLE_CONTENT_TYPE.data, ACCEPTABLE_CONTENT_TYPE.len) == 0) { - LOG_DEBUG("Content-Type is ok"); - return HANDLER_SUCCESS; - } - - LOG_WARN("Content-Type is not acceptable, '%s'", content_type); - - return HANDLER_HTTP_NOT_ACCEPTABLE; -} - -static fcgi_handler_status_t -fcgi_request_load_data(const FCGX_Request* request, int content_length, char** content) -{ - LOG_TRACE("fcgi_request_load_data enter"); - - char* data = malloc(content_length + 1); - if (data == NULL) { - LOG_ERROR("Could not allocate memory for request body (%d bytes)", content_length + 1); - return HANDLER_ERROR; - } - - int actual_content_length = FCGX_GetStr(data, content_length, request->in); - - if (actual_content_length != content_length) { - LOG_ERROR("fcgi_request_load_data: " - "actual_content_length(%d) != content_length(%d); content: '%.*s'", - actual_content_length, content_length, - actual_content_length, data); - free(data); - return HANDLER_HTTP_BAD_REQUEST; - } - - data[content_length] = '\0'; - *content = data; - - LOG_DEBUG("content_length: %d", content_length); - LOG_DEBUG("content: '%s'", *content); - - LOG_TRACE("fcgi_request_load_data exit"); - - return HANDLER_SUCCESS; -} - static int sign_content(const sign_service_t *hsign, fcgi_sign_request_t *req_info) { From 17d40a464e7d37a7aa64a731e24c7db5805a9bd8 Mon Sep 17 00:00:00 2001 From: alashkova Date: Fri, 11 Oct 2024 16:15:50 +0300 Subject: [PATCH 03/37] =?UTF-8?q?SUPPORT-8592.=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20base64=5Fdecoded=5Flength()=20?= =?UTF-8?q?=D0=B8=20decode=5Fbase64=5Furl()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/base64.c | 89 +++++++++++++++++++++++++++++++++++++++++++++- src/utils/base64.h | 2 ++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/utils/base64.c b/src/utils/base64.c index cd4de4f..4c9381e 100644 --- a/src/utils/base64.c +++ b/src/utils/base64.c @@ -1,5 +1,7 @@ #include "base64.h" +#include "logger.h" + #include #include @@ -79,4 +81,89 @@ encode_base64_url(str_t *dst, const str_t *src) dst->data[n * 4 + 3] = basis64_url[d]; } } -} \ No newline at end of file +} + +static void +init_decode_base64_url(unsigned char **table) +{ + static unsigned char basis64_url[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, + 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + assert(table != NULL); + + *table = basis64_url; +} + +int +decode_base64_url(str_t *dst, const str_t *src) +{ + uint32_t value = 0; + unsigned int c; + size_t i; + size_t n = 0; + uint8_t *p = NULL; + + assert(dst != NULL && !str_t_is_null(*dst)); + assert(src != NULL && !str_t_is_null(*src)); + + if ((src->len % 4) == 1) { + LOG_ERROR("decode_base64_url failed. Desc: invalid src length (%zd)", src->len); + goto error; + } + + unsigned char *basis64_url; + init_decode_base64_url(&basis64_url); + + p = (uint8_t *) dst->data; + + for (i = 0; i < src->len; i++) { + c = (unsigned int) src->data[i]; + + if (c < 128 && basis64_url[c] < 64) { + value = (value << 6) | basis64_url[c]; + + if ((i % 4) == 3) { + if (p != NULL) { + p[n] = (value >> 16) & 0xFF; + p[n + 1] = (value >> 8) & 0xFF; + p[n + 2] = value & 0xFF; + } + + n += 3; + value = 0; + } + } else { + LOG_ERROR("decode_base64_url failed. Desc: invalid character: %c", src->data[i]); + goto error; + } + } + + if ((src->len % 4) == 2) { + if (p != NULL) { + p[n] = (value >> 4) & 0xFF; + } + n++; + } else if ((src->len % 4) == 3) { + if (p != NULL) { + p[n] = (value >> 10) & 0xFF; + p[n + 1] = (value >> 2) & 0xFF; + } + n += 2; + } + + dst->len = n; + + return 0; + +error: + LOG_ERROR("decode_base64_url exit with error"); + return -1; +} diff --git a/src/utils/base64.h b/src/utils/base64.h index 55b199d..6ec70e4 100644 --- a/src/utils/base64.h +++ b/src/utils/base64.h @@ -4,7 +4,9 @@ #include "str_t.h" #define base64_encoded_length(len) (((len + 2) / 3) * 4) +#define base64_decoded_length(len) (((len + 3) / 4) * 3) void encode_base64_url(str_t *dst, const str_t *src); +int decode_base64_url(str_t *dst, const str_t *src); #endif // BASE64_H \ No newline at end of file From a221960fbfca18a82d75f7a434b5357e3746fb92 Mon Sep 17 00:00:00 2001 From: alashkova Date: Fri, 11 Oct 2024 16:18:17 +0300 Subject: [PATCH 04/37] =?UTF-8?q?SUPPORT-8592.=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20hex=5Fto=5Fbin()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/str_t.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ src/utils/str_t.h | 2 ++ 2 files changed, 50 insertions(+) diff --git a/src/utils/str_t.c b/src/utils/str_t.c index ac47a7c..76bb317 100644 --- a/src/utils/str_t.c +++ b/src/utils/str_t.c @@ -49,3 +49,51 @@ str_t_to_string(const str_t* str) return string; } + + +static int +char2int(char input) +{ + if (input >= '0' && input <= '9') { + return input - '0'; + } + + if (input >= 'A' && input <= 'F') { + return input - 'A' + 10; + } + + if (input >= 'a' && input <= 'f') { + return input - 'a' + 10; + } + + LOG_ERROR("char2int exit with error: invalid argument, '%c'", input); + assert("char2int exit with error: invalid argument"); + return -1; +} + + +int +hex_to_bin(const str_t* hex, /*out*/ str_t* bin) +{ + bin->len = hex->len / 2; + bin->data = malloc(bin->len + 1); + if (bin->data == NULL) { + LOG_ERROR("Could not allocate memory for bin value (%zd bytes)", bin->len + 1); + goto error; + } + + const char* src = hex->data; + char* dst = bin->data; + + while (*src && src[1]) { + *(dst++) = char2int(*src)*16 + char2int(src[1]); + src += 2; + } + + return 0; + +error: + str_t_clear(bin); + LOG_ERROR("hex2bin exit with error"); + return -1; +} \ No newline at end of file diff --git a/src/utils/str_t.h b/src/utils/str_t.h index 6b28404..3e1707f 100644 --- a/src/utils/str_t.h +++ b/src/utils/str_t.h @@ -92,4 +92,6 @@ int str_t_copy(str_t* dst, const str_t* src); char* str_t_to_string(const str_t* str); +int hex_to_bin(const str_t* hex, /*out*/ str_t* bin); + #endif // STR_T_H_INCLUDED From 17aec4de8bdd3ea846ab196829b9ba0c7d86bfc7 Mon Sep 17 00:00:00 2001 From: alashkova Date: Tue, 15 Oct 2024 13:47:38 +0300 Subject: [PATCH 05/37] =?UTF-8?q?SUPPORT-8592.=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20json-=D0=BF=D0=B0=D1=80=D1=81=D0=B5?= =?UTF-8?q?=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 24 ++++ src/utils/gconf_file.c | 62 +---------- src/utils/glib_utils.c | 49 ++++++++ src/utils/glib_utils.h | 32 ++++++ src/utils/json_parser.c | 239 ++++++++++++++++++++++++++++++++++++++++ src/utils/json_parser.h | 31 ++++++ 6 files changed, 376 insertions(+), 61 deletions(-) create mode 100644 src/utils/glib_utils.c create mode 100644 src/utils/glib_utils.h create mode 100644 src/utils/json_parser.c create mode 100644 src/utils/json_parser.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ce7aee..084a4d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,8 +49,30 @@ SET (DEP_LIBS -lfcgi -lglib-2.0 -ldl + -ljson-glib-1.0 + -lgobject-2.0 ) +# JSON-GLIB +MESSAGE ("") +MESSAGE ("Try to find the JSON-glib-1.0..") +pkg_check_modules (JSONGLIB REQUIRED json-glib-1.0) + +IF (NOT JSONGLIB_FOUND) + MESSAGE(SEND_ERROR "Can not find json-glib-1.0") +ELSE () + MESSAGE ("json-glib-1.0 is found: ") + MESSAGE ("JSONGLIB_LIB_INCLUDE_DIR : " ${JSONGLIB_LIB_INCLUDE_DIR}) + MESSAGE ("JSONGLIB_INCLUDE_DIR : " ${JSONGLIB_INCLUDE_DIR}) + MESSAGE ("JSONGLIB_LIBRARY_DIR : " ${JSONGLIB_LIBRARY_DIR}) + MESSAGE ("JSONGLIB_INCLUDE_DIRS : " ${JSONGLIB_INCLUDE_DIRS}) + MESSAGE ("JSONGLIB_LIBRARY_DIRS : " ${JSONGLIB_LIBRARY_DIRS}) + MESSAGE ("JSONGLIB_LIBRARIES : " ${JSONGLIB_LIBRARIES}) +ENDIF (NOT JSONGLIB_FOUND) +MESSAGE ("") + +INCLUDE_DIRECTORIES ("${JSONGLIB_INCLUDE_DIRS}") + # version.h CONFIGURE_FILE (${SOURCE_DIR}/version.h.in ${SOURCE_DIR}/version.h) @@ -88,6 +110,8 @@ ADD_EXECUTABLE (${PROJECT_NAME} ${UTILS_DIR}/conf_file_context.c ${UTILS_DIR}/cryptopro.c ${UTILS_DIR}/gconf_file.c + ${UTILS_DIR}/glib_utils.c + ${UTILS_DIR}/json_parser.c ${UTILS_DIR}/library.c ${UTILS_DIR}/logger.c ${UTILS_DIR}/str_t.c diff --git a/src/utils/gconf_file.c b/src/utils/gconf_file.c index ecd57f7..4bcfc11 100644 --- a/src/utils/gconf_file.c +++ b/src/utils/gconf_file.c @@ -1,69 +1,9 @@ #include "gconf_file.h" +#include "glib_utils.h" #include "logger.h" #include -/** Returns the length of the gchar string @str - */ -static inline size_t gstr_byte_len(const gchar* str) -{ - return strlen((char*) str); -} - -/* - * Copies the gchar string pointed by src into the content pointed by dst, including - * the terminating null character (and stopping at that point). - * Uses to avoid dependence from glib. - * @src: gchar string to be copied - * @dst: pointer to destination of type str_t where content is to be copied - * Return value: 0 - success, -1 - error - */ -static int -copy_gchar2strt(const gchar* src, str_t* dst) -{ - assert(src != NULL); - assert(dst != NULL); - - size_t len = gstr_byte_len(src); - char* data = (char*) malloc(len + 1); - if (data == NULL) { - LOG_ERROR("gchar2strt exit with error: could not allocate memory for dst"); - return -1; - } - - dst->data = data; - dst->len = len; - memcpy(dst->data, src, dst->len + 1); - dst->data[dst->len] = '\0'; - return 0; -} - - -/* - * Copies the gchar string pointed by src into the content pointed by return value, - * including the terminating null character (and stopping at that point). - * Uses to avoid dependence from glib. - * @src: gchar string to be copied - * Return value: pointer to destination where content is to be copied - */ -static char* -copy_gchar2char(const gchar* src) -{ - assert(src != NULL); - - // char* tmp_end = strchr(src, '\0'); - char* dst = NULL; - size_t len = gstr_byte_len(src); - dst = (char*) malloc(len + 1); - if (dst == NULL) { - LOG_ERROR("gchar2char exit with error: could not allocate memory for dst"); - return NULL; - } - memcpy(dst, src, len + 1); - return dst; -} - - /* Returns the string value from GKeyFile associated with key under section and converted to str_t * @ini_file: GKeyFile * @section: section name in config file diff --git a/src/utils/glib_utils.c b/src/utils/glib_utils.c new file mode 100644 index 0000000..b08797b --- /dev/null +++ b/src/utils/glib_utils.c @@ -0,0 +1,49 @@ +#include "glib_utils.h" + +#include "logger.h" + +#include + +/** Returns the length of the gchar string @str + */ +static inline size_t +gstr_byte_len(const gchar* str) +{ + return strlen((char*) str); +} + +int +copy_gchar2strt(const gchar* src, str_t* dst) +{ + assert(src != NULL); + assert(dst != NULL); + + size_t len = gstr_byte_len(src); + char* data = (char*) malloc(len + 1); + if (data == NULL) { + LOG_ERROR("gchar2strt exit with error: could not allocate memory for dst"); + return -1; + } + + dst->data = data; + dst->len = len; + memcpy(dst->data, src, dst->len + 1); + dst->data[dst->len] = '\0'; + return 0; +} + +char* +copy_gchar2char(const gchar* src) +{ + assert(src != NULL); + + char* dst = NULL; + size_t len = gstr_byte_len(src); + dst = (char*) malloc(len + 1); + if (dst == NULL) { + LOG_ERROR("gchar2char exit with error: could not allocate memory for dst"); + return NULL; + } + memcpy(dst, src, len + 1); + return dst; +} \ No newline at end of file diff --git a/src/utils/glib_utils.h b/src/utils/glib_utils.h new file mode 100644 index 0000000..3873e1d --- /dev/null +++ b/src/utils/glib_utils.h @@ -0,0 +1,32 @@ +#ifndef GLIB_UTULS_H_INCLUDED +#define GLIB_UTULS_H_INCLUDED + +#include "str_t.h" + +#include + + +/* + * Copies the gchar string pointed by src into the content pointed by dst, including + * the terminating null character (and stopping at that point). + * Uses to avoid dependence from glib. + * @src: gchar string to be copied + * @dst: pointer to destination of type str_t where content is to be copied + * Return value: 0 - success, -1 - error + */ +int +copy_gchar2strt(const gchar* src, str_t* dst); + + +/* + * Copies the gchar string pointed by src into the content pointed by return value, + * including the terminating null character (and stopping at that point). + * Uses to avoid dependence from glib. + * @src: gchar string to be copied + * Return value: pointer to destination where content is to be copied + */ +char* +copy_gchar2char(const gchar* src); + + +#endif // GLIB_UTULS_H_INCLUDED diff --git a/src/utils/json_parser.c b/src/utils/json_parser.c new file mode 100644 index 0000000..8f2b797 --- /dev/null +++ b/src/utils/json_parser.c @@ -0,0 +1,239 @@ +#include "json_parser.h" + +#include "glib_utils.h" +#include "logger.h" + +#include +#include + + +typedef struct json_parser_s { + JsonParser *parser; + void *root; + json_parser_type root_type; + +} json_parser_t; + + +static JsonObject* get_object_from_node(JsonNode *node); +static int json_get_object_member_as_string(Hjson_parser hparser, const char* field_name, + /*out*/ str_t* field_value); + + +Hjson_parser +json_parser_create(const char* data, size_t data_len) +{ + GError* gerror = NULL; + JsonNode* json_node = NULL; + JsonNodeType json_node_type; + const char *desc = ""; + + LOG_TRACE("json_parser_create enter"); + + assert(data != NULL && data_len != 0 && "json data is empty"); + + json_parser_t* parser = calloc (sizeof(json_parser_t), 1); + if (parser == NULL) { + desc = "Could not allocate memory for json_parser_t"; + goto error; + } + + parser->parser = json_parser_new(); + if (parser->parser == NULL) { + desc = "Could not create JsonParser"; + goto error; + } + + LOG_DEBUG("json_parser_create. json data: %.*s", (int)data_len, data); + + if (json_parser_load_from_data(parser->parser, data, data_len, &gerror) != TRUE) { + LOG_ERROR("json_parser_create. json_parser_load_from_data failed: %s", gerror->message); + g_error_free(gerror); + goto error; + } + + /* get root node */ + json_node = json_parser_get_root(parser->parser); + if (json_node == NULL) { + desc = "Could not parse json object: json_parser_get_root failed"; + goto error; + } + + json_node_type = json_node_get_node_type(json_node); + if (json_node_type != JSON_NODE_OBJECT) { + desc = "Could not parse json object: unexpected root type"; + goto error; + } + + parser->root_type = JSON_PARSER_TYPE_OBJECT; + + /* try to get root as object */ + parser->root = get_object_from_node(json_node); + if (parser->root == NULL) { + desc = "Could not parse json object: json_node_get_object failed"; + goto error; + } + + return (Hjson_parser)parser; + +error: + json_parser_free((Hjson_parser)parser); + LOG_ERROR("json_parser_create exit with error. Desc: %s", desc); + return NULL;; +} + + +void +json_parser_free(Hjson_parser hparser) +{ + json_parser_t* parser = (json_parser_t*) hparser; + + if (parser == NULL) return; + + if (parser->parser != NULL) { + g_object_unref(parser->parser); + } + + free(parser); +} + + +int +json_get_string_member(Hjson_parser hparser, const char* field_name, + /*out*/ str_t* field_value) +{ + LOG_TRACE("json_get_string_member enter"); + + switch (json_get_object_member_as_string(hparser, field_name, field_value)) { + case 0: + LOG_TRACE("json_get_string_member exit successfully. field_value: %.*s", + (int) field_value->len, + field_value->data); + return 0; + case 1: + LOG_ERROR("Could not retrieve '%.*s' from json object. " + "Desc: field does not exist or it has not proper type. " + "Expected: string member", + (int) field_value->len, field_value->data); + break; + default: + LOG_ERROR("json_get_object_member_as_string failed, member: '%.*s'", + (int) field_value->len, field_value->data); + break; + + } + + LOG_ERROR("json_get_string_member exit with error"); + return -1; +} + + +static JsonObject* +get_object_from_node(JsonNode *node) +{ + JsonObject *ret = NULL; + + if (node == NULL) { + LOG_WARN("get_object_from_node. node is NULL"); + return NULL; + } + + if (!JSON_NODE_HOLDS_OBJECT(node)) { + LOG_ERROR("Bad json scheme. Expected node: object"); + return NULL; + } + + ret = json_node_get_object(node); + assert(ret != NULL && "Could not extract the node of correct type"); + + return ret; +} + + +static int +get_string_from_node(JsonNode *node, /*out*/ const gchar **value) +{ + assert(value != NULL); + + *value = NULL; + + if (node == NULL) { + LOG_WARN("get_string_from_node. node is NULL"); + return 0; + } + + /* если нод содержит нулевое значение, оставляем наш указатель так же нулевым */ + if (JSON_NODE_HOLDS_NULL(node)) { + LOG_DEBUG("get_string_from_node. node holds NULL"); + return 0; + } + + if (!JSON_NODE_HOLDS_VALUE(node) || G_TYPE_STRING != json_node_get_value_type(node)) { + LOG_ERROR("Bad json scheme. Expected node: string value. Actual node type: %s", + json_node_type_name(node)); + return -1; + } + + *value = json_node_get_string(node); + if (*value == NULL) { + LOG_ERROR("Bad json node. Expected node: string value"); + return -1; + } + + return 0; +} + + +static int +json_object_get_string(JsonObject *object, const gchar *key, /*out*/const gchar** value) +{ + JsonNode *node = NULL; + + LOG_TRACE("json_object_get_string"); + + assert(object != NULL); + assert(key != NULL); + assert(value != NULL); + + *value = NULL; + + node = json_object_get_member(object, key); + + if (node == NULL) { + LOG_DEBUG("object member by key '%s' is not exists", key); + /* ret = NULL; */ + return 0; + } + + return get_string_from_node(node, value); +} + + +static int +json_get_object_member_as_string(Hjson_parser hparser, const char* field_name, + /*out*/ str_t* field_value) +{ + json_parser_t* parser = (json_parser_t*) hparser; + const gchar* value = NULL; + + assert(parser != NULL); + assert(parser->root_type == JSON_PARSER_TYPE_OBJECT); + assert(parser->root != NULL); + assert(field_name != NULL); + assert(field_value != NULL && str_t_is_null(*field_value)); + + LOG_DEBUG("json_get_object_member_as_string. field name: %s", field_name); + + if (json_object_get_string((JsonObject*) (parser->root), field_name, &value)) { + return -1; + } + if (value == NULL) { + return 1; + } + if (copy_gchar2strt(value, field_value) != 0) { + LOG_ERROR("json_get_object_member_as_string. Could not copy field_value"); + return -1; + } + + return 0; +} diff --git a/src/utils/json_parser.h b/src/utils/json_parser.h new file mode 100644 index 0000000..a99ade1 --- /dev/null +++ b/src/utils/json_parser.h @@ -0,0 +1,31 @@ +#ifndef JSON_PARSER_H_INCLUDED +#define JSON_PARSER_H_INCLUDED + +#include "str_t.h" + +#include +#include + + +typedef struct json_parser_t *Hjson_parser; + + +typedef enum json_parser_type_e { + JSON_PARSER_TYPE_UNKNOWN, + JSON_PARSER_TYPE_STRING, + JSON_PARSER_TYPE_OBJECT, + JSON_PARSER_TYPE_ARRAY + +} json_parser_type; + + +Hjson_parser json_parser_create(const char* data, size_t data_len); + +void json_parser_free(Hjson_parser hparser); + + +int json_get_string_member(Hjson_parser hparser, const char* field_name, + /*out*/ str_t* field_value); + + +#endif // JSON_PARSER_H_INCLUDED From 503287af7f0dab852919537d8cba593cf72a20c9 Mon Sep 17 00:00:00 2001 From: alashkova Date: Tue, 15 Oct 2024 15:46:30 +0300 Subject: [PATCH 06/37] =?UTF-8?q?SUPPORT-8592.=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20cryptopro=5Fverify()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/capi.c | 6 ++ src/utils/capi.h | 60 ++++++++++++++ src/utils/cryptopro.c | 187 +++++++++++++++++++++++++++++++++++++++++- src/utils/cryptopro.h | 3 + 4 files changed, 255 insertions(+), 1 deletion(-) diff --git a/src/utils/capi.c b/src/utils/capi.c index 7ecf64b..8cba246 100644 --- a/src/utils/capi.c +++ b/src/utils/capi.c @@ -26,11 +26,17 @@ capi_function_list_init(library_t *lib, capi_function_list_t *fl) LIBRARY_RESOLVE(fl->CryptReleaseContext, lib, "CryptReleaseContext"); LIBRARY_RESOLVE(fl->CryptSignMessage, lib, "CryptSignMessage"); LIBRARY_RESOLVE(fl->GetLastError, lib, "GetLastError"); + LIBRARY_RESOLVE(fl->CryptImportPublicKeyInfo, lib, "CryptImportPublicKeyInfo"); + LIBRARY_RESOLVE(fl->CryptDestroyKey, lib, "CryptDestroyKey"); #ifdef UNICODE LIBRARY_RESOLVE(fl->CryptSignHash, lib, "CryptSignHashW"); + LIBRARY_RESOLVE(fl->CryptVerifySignature, lib, "CryptVerifySignatureW"); + LIBRARY_RESOLVE(fl->CryptAcquireContext, lib, "CryptAcquireContextW"); #else LIBRARY_RESOLVE(fl->CryptSignHash, lib, "CryptSignHashA"); + LIBRARY_RESOLVE(fl->CryptVerifySignature, lib, "CryptVerifySignatureA"); + LIBRARY_RESOLVE(fl->CryptAcquireContext, lib, "CryptAcquireContextA"); #endif // !UNICODE return true; diff --git a/src/utils/capi.h b/src/utils/capi.h index 9277014..555ffb9 100644 --- a/src/utils/capi.h +++ b/src/utils/capi.h @@ -124,10 +124,65 @@ DECLARE_FN(WINADVAPI, BYTE *pbSignature, DWORD *pdwSigLen)); +DECLARE_FN(WINADVAPI, + BOOL, + CRYPT_VERIFY_SIGNATURE_A, + (HCRYPTHASH hHash, + CONST BYTE *pbSignature, + DWORD dwSigLen, + HCRYPTKEY hPubKey, + LPCSTR szDescription, + DWORD dwFlags)); + +DECLARE_FN(WINADVAPI, + BOOL, + CRYPT_VERIFY_SIGNATURE_W, + (HCRYPTHASH hHash, + CONST BYTE *pbSignature, + DWORD dwSigLen, + HCRYPTKEY hPubKey, + LPCWSTR szDescription, + DWORD dwFlags)); + +DECLARE_FN(WINADVAPI, + BOOL, + CRYPT_ACQUIRE_CONTEXT_A, + (HCRYPTPROV *phProv, + LPCSTR pszContainer, + LPCSTR pszProvider, + DWORD dwProvType, + DWORD dwFlags)); + +DECLARE_FN(WINADVAPI, + BOOL, + CRYPT_ACQUIRE_CONTEXT_W, + (HCRYPTPROV *phProv, + LPCSTR pszContainer, + LPCSTR pszProvider, + DWORD dwProvType, + DWORD dwFlags)); + +DECLARE_FN(WINADVAPI, + BOOL, + CRYPT_IMPORT_PUBLIC_KEY_INFO, + (HCRYPTPROV hCryptProv, + DWORD dwCertEncodingType, + PCERT_PUBLIC_KEY_INFO pInfo, + HCRYPTKEY *phKey)); + +DECLARE_FN(WINADVAPI, + BOOL, + CRYPT_DESTROY_KEY, + (HCRYPTKEY hKey)); + #ifdef UNICODE #define CRYPT_SIGN_HASH_FN CRYPT_SIGN_HASH_W_FN +#define CRYPT_VERIFY_SIGNATURE_FN CRYPT_VERIFY_SIGNATURE_W_FN +#define CRYPT_ACQUIRE_CONTEXT_FN CRYPT_ACQUIRE_CONTEXT_W_FN #else #define CRYPT_SIGN_HASH_FN CRYPT_SIGN_HASH_A_FN +#define CRYPT_VERIFY_SIGNATURE_FN CRYPT_VERIFY_SIGNATURE_A_FN +#define CRYPT_ACQUIRE_CONTEXT_FN CRYPT_ACQUIRE_CONTEXT_A_FN #endif // !UNICODE @@ -146,6 +201,11 @@ typedef struct { CERT_FIND_CERTIFICATE_IN_STORE_FN CertFindCertificateInStore; CERT_GET_CERTIFICATE_CONTEXT_PROPERTY_FN CertGetCertificateContextProperty; CERT_SET_CERTIFICATE_CONTEXT_PROPERTY_FN CertSetCertificateContextProperty; + CRYPT_VERIFY_SIGNATURE_FN CryptVerifySignature; + CRYPT_ACQUIRE_CONTEXT_FN CryptAcquireContext; + CRYPT_IMPORT_PUBLIC_KEY_INFO_FN CryptImportPublicKeyInfo; + CRYPT_DESTROY_KEY_FN CryptDestroyKey; + } capi_function_list_t; diff --git a/src/utils/cryptopro.c b/src/utils/cryptopro.c index a3b54d3..4cbb257 100644 --- a/src/utils/cryptopro.c +++ b/src/utils/cryptopro.c @@ -292,4 +292,189 @@ set_pin(PCCERT_CONTEXT pSignerCert, const str_t *pin) error: LOG_ERROR("set_pin exit with error. Last error code: 0x%08x", cp_function_list.GetLastError()); return -1; -} \ No newline at end of file +} + +static PCCERT_CONTEXT +get_cert_by_thumbprint(HCERTSTORE hStoreHandle, const str_t* thumbprint) +{ + str_t thumbprint_bin = str_t_null; + + if (hex_to_bin(thumbprint, &thumbprint_bin)) { + goto error; + } + + CRYPT_HASH_BLOB hash = { + .pbData = (BYTE*)thumbprint_bin.data, + .cbData = thumbprint_bin.len + }; + + PCCERT_CONTEXT cert = cp_function_list.CertFindCertificateInStore(hStoreHandle, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, + CERT_FIND_HASH, + &hash, + NULL); + if (cert == NULL) { + LOG_ERROR("Could not find certificate in store"); + goto error; + } + + str_t_clear(&thumbprint_bin); + + return cert; + +error: + str_t_clear(&thumbprint_bin); + LOG_ERROR("get_cert_by_thumbprint exit with error"); + return NULL; +} + +static int +alg_id_from_str(const str_t* alg, /*out*/ ALG_ID* alg_id) +{ + LOG_TRACE("alg_id_from_str"); + + if (strncmp(alg->data, "GOST3410_2012_256", alg->len) == 0) { + *alg_id = CALG_GR3411_2012_256; + return 0; + } + if (strncmp(alg->data, "GOST3410_2012_512", alg->len) == 0) { + *alg_id = CALG_GR3411_2012_512; + return 0; + } + if (strncmp(alg->data, "RS256", alg->len) == 0) { + *alg_id = CALG_SHA_256; + return 0; + } + + LOG_ERROR("Unknown alg: '%.*s'", (int) alg->len, alg->data); + LOG_ERROR("alg_id_from_str exit with error"); + return -1; +} + +static void +log_verify_error() +{ + DWORD err = cp_function_list.GetLastError(); + + switch (err) { + case NTE_BAD_SIGNATURE: + LOG_WARN("sign is invalid: bad signature (0x%08x)", err); + break; + + case NTE_BAD_ALGID: + LOG_WARN("sign is invalid: bad alg id (0x%08x)", err); + break; + + default: + LOG_WARN("sign is invalid. Last error code: 0x%08x", err); + break; + } +} + +int +cryptopro_verify(const str_t* cert_thumbprint, const str_t* alg, const str_t *data, + const str_t *sign, bool* is_verified) +{ + int rc = -1; + HCERTSTORE hStoreHandle = NULL; + PCCERT_CONTEXT certificate = NULL; + HCRYPTPROV hCryptProv = 0; + HCRYPTHASH hash = 0; + HCRYPTKEY hPubKey = 0; + str_t sign_reversed = str_t_null; + ALG_ID alg_id; + + LOG_TRACE("cryptopro_verify enter"); + + *is_verified = false; + + if (alg_id_from_str(alg, &alg_id)) { + goto exit; + } + + if (reverse_sign(sign, &sign_reversed)) { + goto exit; + } + + hStoreHandle = cert_open_store(); + if (hStoreHandle == NULL) { + goto exit; + } + + certificate = get_cert_by_thumbprint(hStoreHandle, cert_thumbprint); + if (certificate == NULL) { + goto exit; + } + + if (!cp_function_list.CryptAcquireContext(&hCryptProv, NULL, CP_KC1_GR3410_2001_PROV, + PROV_GOST_2001_DH, CRYPT_VERIFYCONTEXT)) { + LOG_ERROR("CryptAcquireContext() failed"); + goto exit; + } + + if (!cp_function_list.CryptImportPublicKeyInfo(hCryptProv, X509_ASN_ENCODING, + &certificate->pCertInfo->SubjectPublicKeyInfo, + &hPubKey)) { + LOG_ERROR("CryptImportPublicKeyInfo() failed"); + goto exit; + } + + if (!cp_function_list.CryptCreateHash(hCryptProv, alg_id, 0, 0, &hash)) { + LOG_ERROR("CryptCreateHash() failed"); + goto exit; + } + + if (!cp_function_list.CryptHashData(hash, (CONST BYTE *) data->data, data->len, 0)) { + LOG_ERROR("CryptHashData() failed"); + goto exit; + } + + if (cp_function_list.CryptVerifySignature(hash, + (CONST BYTE *) sign_reversed.data, + sign_reversed.len, + hPubKey, + NULL, + 0)) { + LOG_DEBUG("sign is valid"); + *is_verified = true; + } else { + log_verify_error(); + } + + rc = 0; + +exit: + str_t_clear(&sign_reversed); + + if (hash) { + cp_function_list.CryptDestroyHash(hash); + } + + if (hPubKey) { + cp_function_list.CryptDestroyKey(hPubKey); + } + + if (hCryptProv) { + cp_function_list.CryptReleaseContext(hCryptProv, 0); + } + + if (certificate) { + cp_function_list.CertFreeCertificateContext(certificate); + } + + if (hStoreHandle) { + if (!cp_function_list.CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_CHECK_FLAG)) { + LOG_ERROR("CertCloseStore() failed"); + } + } + + if (rc == 0) { + LOG_TRACE("cryptopro_verify exit"); + } else { + LOG_ERROR("cryptopro_verify exit with error. Last error code: 0x%08x", + cp_function_list.GetLastError()); + } + + return rc; +} diff --git a/src/utils/cryptopro.h b/src/utils/cryptopro.h index a580216..e7efdc5 100644 --- a/src/utils/cryptopro.h +++ b/src/utils/cryptopro.h @@ -26,4 +26,7 @@ bool cryptopro_init(const char* cp_file); int cryptopro_sign(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t *sign); +int cryptopro_verify(const str_t* cert_thumbprint, const str_t* alg, const str_t *data, + const str_t *sign, bool* is_verified); + #endif // CRYPTOPRO_H_INCLUDED From b157ea6a0c5cb173b3cd7b6c1179ab2e57903a39 Mon Sep 17 00:00:00 2001 From: alashkova Date: Wed, 16 Oct 2024 11:44:16 +0300 Subject: [PATCH 07/37] =?UTF-8?q?SUPPORT-8592.=20=D0=A0=D0=B5=D0=B0=D0=BB?= =?UTF-8?q?=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=20service=5Fverify,=20?= =?UTF-8?q?=D0=BA=D0=BE=D1=82=D0=BE=D1=80=D1=8B=D0=B9=20=D0=BE=D0=B1=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=B0=D1=82=D1=8B=D0=B2=D0=B0=D0=B5=D1=82=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D1=8B=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B8=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=B4=D0=BF=D0=B8=D1=81=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/ervu-sign-module.conf.example | 4 + src/modules/service_verify.c | 338 +++++++++++++++++++++++++++++ src/modules/service_verify.h | 25 +++ src/service_manager.c | 21 ++ src/service_manager.h | 3 + src/utils/cryptopro.c | 2 + 6 files changed, 393 insertions(+) create mode 100644 src/modules/service_verify.c create mode 100644 src/modules/service_verify.h diff --git a/conf/ervu-sign-module.conf.example b/conf/ervu-sign-module.conf.example index b6b307b..f50d665 100644 --- a/conf/ervu-sign-module.conf.example +++ b/conf/ervu-sign-module.conf.example @@ -11,3 +11,7 @@ fcgi_listen_host = 127.0.0.1 #location = /sign signer_subject = signer@example.ru pin = **** + +[verify] +#location = /verify +esia_cert_thumbprint = diff --git a/src/modules/service_verify.c b/src/modules/service_verify.c new file mode 100644 index 0000000..333903b --- /dev/null +++ b/src/modules/service_verify.c @@ -0,0 +1,338 @@ +#include "service_verify.h" + +#include "fcgisrv/fcgi_utils.h" + +#include "utils/base64.h" +#include "utils/cryptopro.h" +#include "utils/json_parser.h" + + +#define VERIFY_CONF_SECTION "verify" +#define VERIFY_CONF_KEY_LOCATION "location" +#define VERIFY_CONF_KEY_THUMBPRINT "esia_cert_thumbprint" + +static const str_t VERIFY_CONF_DEFAULT_LOCATION = str_t_const("/verify"); + +static const int CLIENT_MAX_BODY_SIZE = 4096; +static const char* ACCEPTABLE_CONTENT_TYPE = "text/plain"; + +static const char* JWT_ALG_FIELD_NAME = "alg"; + + +typedef struct verify_service_s { + const verify_conf_t *conf; + +} verify_service_t; + + +typedef struct fcgi_verify_request_s { + char *content; + int content_length; + +} fcgi_verify_request_t; + + +static fcgi_request_handler_pt fcgi_request_finalize_handler(fcgi_handler_status_t status); +static void fcgi_verify_request_clear(fcgi_verify_request_t *req_info); +static fcgi_handler_status_t verify_jwt_sign(const fcgi_verify_request_t* req_info, + const verify_service_t *ctx); + + +int +verify_conf_load(verify_conf_t *conf, const conf_file_context_t conf_file) +{ + LOG_TRACE("verify_conf_load enter"); + + memset(conf, 0, sizeof(verify_conf_t)); + + conf_file_field_t fields[] = { + { + VERIFY_CONF_SECTION, + VERIFY_CONF_KEY_LOCATION, + &conf->location, + CONF_FILE_VALUE_STRT, + CONF_FILE_VALUE_LOCATION, + &VERIFY_CONF_DEFAULT_LOCATION + }, + { + VERIFY_CONF_SECTION, + VERIFY_CONF_KEY_THUMBPRINT, + &conf->esia_cert_thumbprint, + CONF_FILE_VALUE_STRT, + CONF_FILE_VALUE_NONE, + NULL + }, + }; + + if (conf_file_load_values(conf_file, fields, sizeof(fields) / sizeof(conf_file_field_t))) { + goto error; + } + + LOG_TRACE("verify_conf_load exit"); + return 0; + +error: + verify_conf_clear(conf); + LOG_ERROR("verify_conf_load exit with error"); + return -1; +} + +void +verify_conf_clear(verify_conf_t *conf) +{ + LOG_TRACE("verify_conf_clear"); + + if (conf == NULL) return; + + str_t_clear(&conf->location); + str_t_clear(&conf->esia_cert_thumbprint); + + memset(conf, 0, sizeof(verify_conf_t)); +} + +HVerify +verify_service_create(const verify_conf_t *conf) +{ + LOG_TRACE("verify_service_create enter"); + + verify_service_t *hverify = (verify_service_t*) calloc(1, sizeof(verify_service_t)); + if (hverify == NULL) { + LOG_ERROR("Could not allocate memory for HVerify"); + goto error; + } + + hverify->conf = conf; + + LOG_TRACE("verify_service_create exit"); + return (HVerify)hverify; + +error: + LOG_ERROR("verify_service_create exit with error"); + return NULL; +} + +void +verify_service_free(HVerify hverify) +{ + LOG_TRACE("verify_service_free"); + + verify_service_t *ctx = (verify_service_t *) hverify; + + if (ctx == NULL) return; + + free(ctx); +} + +fcgi_handler_status_t +fcgi_verify_handler(FCGX_Request* request, void* ctx) +{ + fcgi_handler_status_t status = HANDLER_ERROR; + fcgi_verify_request_t req_info = {0}; + + LOG_TRACE("fcgi_verify_handler enter"); + + if (request == NULL) { + LOG_ERROR("fcgi_verify_handler exit with error. Desc: request is NULL"); + return HANDLER_ERROR; + } + + req_info.content_length = fcgi_get_content_length(request); + + status = check_content_length(req_info.content_length, CLIENT_MAX_BODY_SIZE); + if (status != HANDLER_SUCCESS) { + goto exit; + } + + status = check_content_type(request, ACCEPTABLE_CONTENT_TYPE); + if (status != HANDLER_SUCCESS) { + goto exit; + } + + status = fcgi_request_load_data(request, req_info.content_length, &req_info.content); + if (status != HANDLER_SUCCESS) { + goto exit; + } + + status = verify_jwt_sign(&req_info, ctx); + +exit: + status = fcgi_request_finalize_handler(status)(request, ctx); + + fcgi_verify_request_clear(&req_info); + + LOG_TRACE("fcgi_verify_handler exit"); + return status; +} + +static fcgi_request_handler_pt +fcgi_request_finalize_handler(fcgi_handler_status_t status) +{ + fcgi_request_handler_pt handler; + + switch (status) { + case HANDLER_SUCCESS: + case HANDLER_HTTP_OK: + handler = fcgi_200_ok_handler; + break; + + case HANDLER_HTTP_BAD_REQUEST: + handler = fcgi_400_bad_request_handler; + break; + + case HANDLER_HTTP_UNAUTHORIZED: + handler = fcgi_401_unauthorized_handler; + break; + + case HANDLER_HTTP_NOT_ACCEPTABLE: + handler = fcgi_406_not_acceptable_handler; + break; + + case HANDLER_HTTP_REQUEST_ENTITY_TOO_LARGE: + handler = fcgi_413_request_entity_too_large_handler; + break; + + case HANDLER_ERROR: + default: + handler = fcgi_500_internal_server_error_handler; + break; + } + + return handler; +} + +static void +fcgi_verify_request_clear(fcgi_verify_request_t *req_info) +{ + LOG_TRACE("fcgi_verify_request_clear"); + + free(req_info->content); +} + +static int +get_jwt_header_payload_and_sign(const str_t *jwt, + /*out*/ str_t *header, + /*out*/ str_t *header_payload, + /*out*/ str_t *sign) +{ + LOG_TRACE("get_jwt_header_payload_and_sign enter"); + + size_t p_start = 0, p_end = 0; + + for (size_t i = 0; i < jwt->len; i++) { + if (jwt->data[i] == '.') { + if (p_start == 0) { + p_start = i + 1; + } else { + p_end = i; + break; + } + } + } + + if (p_start == 0 || p_end == 0) { + LOG_ERROR("Could not parse jwt. p_start = %zd, p_end = %zd", p_start, p_end); + return -1; + } + + header->data = (char*)jwt->data; + header->len = p_start - 1; + + header_payload->data = (char*)jwt->data; + header_payload->len = p_end; + + sign->data = (char*)jwt->data + p_end + 1; + sign->len = jwt->len - p_end - 1; + + LOG_DEBUG("header: %.*s", (int) header->len, header->data); + LOG_DEBUG("header_payload: %.*s", (int) header_payload->len, header_payload->data); + LOG_DEBUG("sign: %.*s", (int) sign->len, sign->data); + + LOG_TRACE("get_jwt_header_payload_and_sign exit"); + + return 0; +} + +static int +get_alg_from_header(const str_t *header, /*out*/ str_t *alg) +{ + LOG_TRACE("get_alg_from_header"); + + Hjson_parser parser = json_parser_create(header->data, header->len); + if (parser == NULL) { + goto error; + } + + if (json_get_string_member(parser, JWT_ALG_FIELD_NAME, alg)) { + goto error; + } + + json_parser_free(parser); + return 0; + +error: + json_parser_free(parser); + LOG_ERROR("get_alg_from_header exit with error"); + return -1; +} + +static fcgi_handler_status_t +verify_jwt_sign(const fcgi_verify_request_t* req_info, const verify_service_t *ctx) +{ + LOG_TRACE("verify_jwt_sign enter"); + + bool is_verified = false; + + str_t jwt = { + .data = req_info->content, + .len = req_info->content_length + }; + str_t sign = str_t_null; + str_t sign_base64 = str_t_null; + str_t header = str_t_null; + str_t header_base64 = str_t_null; + str_t header_payload = str_t_null; + str_t alg = str_t_null; + + if (get_jwt_header_payload_and_sign(&jwt, &header_base64, &header_payload, &sign_base64)) { + goto error; + } + + sign.len = base64_decoded_length(sign_base64.len); + sign.data = calloc(1, sign.len + 1); + if (sign.data == NULL) { + LOG_ERROR("Could not allocate memory for decoded sign (%zd bytes)", sign.len + 1); + goto error; + } + decode_base64_url(&sign, &sign_base64); + + header.len = base64_decoded_length(header_base64.len); + header.data = calloc(1, header.len + 1); + if (header.data == NULL) { + LOG_ERROR("Could not allocate memory for decoded header (%zd bytes)", header.len + 1); + goto error; + } + decode_base64_url(&header, &header_base64); + + if (get_alg_from_header(&header, &alg)) { + goto error; + } + + if (cryptopro_verify(&ctx->conf->esia_cert_thumbprint, &alg, &header_payload, &sign, + &is_verified)) { + goto error; + } + + str_t_clear(&sign); + str_t_clear(&header); + str_t_clear(&alg); + + LOG_TRACE("verify_jwt_sign exit"); + return is_verified ? HANDLER_SUCCESS : HANDLER_HTTP_UNAUTHORIZED; + +error: + str_t_clear(&sign); + str_t_clear(&header); + str_t_clear(&alg); + LOG_TRACE("verify_jwt_sign exit with error"); + return HANDLER_ERROR; +} diff --git a/src/modules/service_verify.h b/src/modules/service_verify.h new file mode 100644 index 0000000..fdf7153 --- /dev/null +++ b/src/modules/service_verify.h @@ -0,0 +1,25 @@ +#ifndef SERVICE_VERIFY_H_INCLUDED +#define SERVICE_VERIFY_H_INCLUDED + +#include "fcgisrv/fcgi_server.h" + +#include "utils/conf_file_context.h" +#include "utils/str_t.h" + +typedef struct verify_service_t* HVerify; + +typedef struct verify_conf_s { + str_t location; + str_t esia_cert_thumbprint; + +} verify_conf_t; + +int verify_conf_load(verify_conf_t *conf, const conf_file_context_t conf_file); +void verify_conf_clear(verify_conf_t *conf); + +HVerify verify_service_create(const verify_conf_t *conf); +void verify_service_free(HVerify hverify); + +fcgi_handler_status_t fcgi_verify_handler(FCGX_Request* request, void* ctx); + +#endif // SERVICE_VERIFY_H_INCLUDED \ No newline at end of file diff --git a/src/service_manager.c b/src/service_manager.c index 4d8a523..e791ea3 100644 --- a/src/service_manager.c +++ b/src/service_manager.c @@ -160,6 +160,11 @@ service_manager_load_conf(const char* config_file_name, service_manager_conf_t* LOG_ERROR("Sign service configuraton loading failed"); goto error; } + + if (verify_conf_load(&services_cf->verify_cf, conf_file)) { + LOG_ERROR("Verify service configuraton loading failed"); + goto error; + } conf_file_close_file(conf_file); @@ -181,6 +186,7 @@ service_manager_clear_conf(service_manager_conf_t* services_cf) assert(services_cf != NULL); sign_conf_clear(&services_cf->sign_cf); + verify_conf_clear(&services_cf->verify_cf); fcgi_clear_conf(&services_cf->fcgi_cf); main_conf_clear(&services_cf->main_cf); } @@ -198,6 +204,8 @@ deinit_services(service_manager_t* services) sign_service_free(services->hsign); + verify_service_free(services->hverify); + LOG_TRACE("deinit_services exit"); return 0; @@ -224,6 +232,7 @@ init_services(service_manager_t* services, const service_manager_conf_t* service goto error; } + /* sign service */ services->hsign = sign_service_create(&services_cf->sign_cf); if (services->hsign == NULL) { goto error; @@ -234,6 +243,18 @@ init_services(service_manager_t* services, const service_manager_conf_t* service LOG_ERROR("Could not register 'sign service'"); goto error; } + + /* verify service */ + services->hverify = verify_service_create(&services_cf->verify_cf); + if (services->hverify == NULL) { + goto error; + } + + if (register_service(services, services_cf, fcgi_verify_handler, services->hverify, + &services_cf->verify_cf.location)) { + LOG_ERROR("Could not register 'verify service'"); + goto error; + } /* run fastcgi server */ if (fcgi_run_server(services->hfcgi) != 0) { diff --git a/src/service_manager.h b/src/service_manager.h index b56c9a2..683fdb7 100644 --- a/src/service_manager.h +++ b/src/service_manager.h @@ -8,6 +8,7 @@ #include "fcgisrv/fcgi_server.h" #include "modules/service_sign.h" +#include "modules/service_verify.h" struct service_s; @@ -46,12 +47,14 @@ typedef struct service_manager_conf_s { fcgi_conf_t fcgi_cf; main_conf_t main_cf; sign_conf_t sign_cf; + verify_conf_t verify_cf; } service_manager_conf_t; typedef struct service_manager_s { HFcgi hfcgi; HSign hsign; + HVerify hverify; } service_manager_t; diff --git a/src/utils/cryptopro.c b/src/utils/cryptopro.c index 4cbb257..3b19603 100644 --- a/src/utils/cryptopro.c +++ b/src/utils/cryptopro.c @@ -299,6 +299,8 @@ get_cert_by_thumbprint(HCERTSTORE hStoreHandle, const str_t* thumbprint) { str_t thumbprint_bin = str_t_null; + LOG_TRACE("get_cert_by_thumbprint"); + if (hex_to_bin(thumbprint, &thumbprint_bin)) { goto error; } From 2f2a6efd89d629aa4330e982ccdb79c06f9f4955 Mon Sep 17 00:00:00 2001 From: alashkova Date: Wed, 16 Oct 2024 15:59:19 +0300 Subject: [PATCH 08/37] SUPPORT-8592. service_verify: CLIENT_MAX_BODY_SIZE=8192 --- CMakeLists.txt | 1 + src/modules/service_verify.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 084a4d5..c9fecbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,6 +121,7 @@ ADD_EXECUTABLE (${PROJECT_NAME} ${FCGISRV_DIR}/fcgi_utils.c ${FCGISRV_DIR}/fcgi_worker.c ${MODULES_DIR}/service_sign.c + ${MODULES_DIR}/service_verify.c ) TARGET_LINK_LIBRARIES (${PROJECT_NAME} ${DEP_LIBS}) diff --git a/src/modules/service_verify.c b/src/modules/service_verify.c index 333903b..1026410 100644 --- a/src/modules/service_verify.c +++ b/src/modules/service_verify.c @@ -13,7 +13,7 @@ static const str_t VERIFY_CONF_DEFAULT_LOCATION = str_t_const("/verify"); -static const int CLIENT_MAX_BODY_SIZE = 4096; +static const int CLIENT_MAX_BODY_SIZE = 8192; static const char* ACCEPTABLE_CONTENT_TYPE = "text/plain"; static const char* JWT_ALG_FIELD_NAME = "alg"; From f95975719f0be2a1e67d392fcdb7ee0ea07f5b2b Mon Sep 17 00:00:00 2001 From: alashkova Date: Wed, 16 Oct 2024 16:01:38 +0300 Subject: [PATCH 09/37] =?UTF-8?q?SUPPORT-8592.=20=D0=92=20=D1=81=D0=BB?= =?UTF-8?q?=D1=83=D1=87=D0=B0=D0=B5=20=D0=BD=D0=B5=D0=B2=D0=B5=D1=80=D0=BD?= =?UTF-8?q?=D0=BE=D0=B9=20=D0=BF=D0=BE=D0=B4=D0=BF=D0=B8=D1=81=D0=B8=20?= =?UTF-8?q?=D0=B2=20=D0=BE=D1=82=D0=B2=D0=B5=D1=82=D0=B5=20=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=B4=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8=20=D0=BE=D1=82=20?= =?UTF-8?q?=D0=BA=D1=80=D0=B8=D0=BF=D1=82=D0=BE=D0=BF=D1=80=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/service_verify.c | 40 ++++++++++++++++++++++++++++----- src/utils/cryptopro.c | 43 +++++++++++++++++++++++++++++------- src/utils/cryptopro.h | 2 +- 3 files changed, 71 insertions(+), 14 deletions(-) diff --git a/src/modules/service_verify.c b/src/modules/service_verify.c index 1026410..63747c7 100644 --- a/src/modules/service_verify.c +++ b/src/modules/service_verify.c @@ -11,6 +11,12 @@ #define VERIFY_CONF_KEY_LOCATION "location" #define VERIFY_CONF_KEY_THUMBPRINT "esia_cert_thumbprint" +#define FCGI_401_BAD_SIGNATURE_RESPONSE_FORMAT \ + "Status: 401 Unauthorized" CRLF\ + "Content-type: text/plain" CRLF\ + CRLF\ + "%s" CRLF + static const str_t VERIFY_CONF_DEFAULT_LOCATION = str_t_const("/verify"); static const int CLIENT_MAX_BODY_SIZE = 8192; @@ -29,12 +35,14 @@ typedef struct fcgi_verify_request_s { char *content; int content_length; + char *verify_error; + } fcgi_verify_request_t; static fcgi_request_handler_pt fcgi_request_finalize_handler(fcgi_handler_status_t status); static void fcgi_verify_request_clear(fcgi_verify_request_t *req_info); -static fcgi_handler_status_t verify_jwt_sign(const fcgi_verify_request_t* req_info, +static fcgi_handler_status_t verify_jwt_sign(fcgi_verify_request_t* req_info, const verify_service_t *ctx); @@ -156,7 +164,7 @@ fcgi_verify_handler(FCGX_Request* request, void* ctx) status = verify_jwt_sign(&req_info, ctx); exit: - status = fcgi_request_finalize_handler(status)(request, ctx); + status = fcgi_request_finalize_handler(status)(request, &req_info); fcgi_verify_request_clear(&req_info); @@ -164,6 +172,27 @@ exit: return status; } +static fcgi_handler_status_t +fcgi_401_bad_signature_handler(const FCGX_Request* request, void *ctx) +{ + LOG_TRACE("fcgi_401_bad_signature_handler"); + + const fcgi_verify_request_t *req_info = (fcgi_verify_request_t*) ctx; + + assert(req_info->verify_error != NULL); + + LOG_DEBUG("response status: " FCGI_401_BAD_SIGNATURE_RESPONSE_FORMAT, + req_info->verify_error); + + if (FCGX_FPrintF(request->out, FCGI_401_BAD_SIGNATURE_RESPONSE_FORMAT, + req_info->verify_error) < 0) { + LOG_ERROR("FCGX_FPrintF() failed"); + return HANDLER_ERROR; + } + + return HANDLER_SUCCESS; +} + static fcgi_request_handler_pt fcgi_request_finalize_handler(fcgi_handler_status_t status) { @@ -180,7 +209,7 @@ fcgi_request_finalize_handler(fcgi_handler_status_t status) break; case HANDLER_HTTP_UNAUTHORIZED: - handler = fcgi_401_unauthorized_handler; + handler = fcgi_401_bad_signature_handler; break; case HANDLER_HTTP_NOT_ACCEPTABLE: @@ -206,6 +235,7 @@ fcgi_verify_request_clear(fcgi_verify_request_t *req_info) LOG_TRACE("fcgi_verify_request_clear"); free(req_info->content); + free(req_info->verify_error); } static int @@ -276,7 +306,7 @@ error: } static fcgi_handler_status_t -verify_jwt_sign(const fcgi_verify_request_t* req_info, const verify_service_t *ctx) +verify_jwt_sign(fcgi_verify_request_t* req_info, const verify_service_t *ctx) { LOG_TRACE("verify_jwt_sign enter"); @@ -318,7 +348,7 @@ verify_jwt_sign(const fcgi_verify_request_t* req_info, const verify_service_t *c } if (cryptopro_verify(&ctx->conf->esia_cert_thumbprint, &alg, &header_payload, &sign, - &is_verified)) { + &is_verified, &req_info->verify_error)) { goto error; } diff --git a/src/utils/cryptopro.c b/src/utils/cryptopro.c index 3b19603..099fbd1 100644 --- a/src/utils/cryptopro.c +++ b/src/utils/cryptopro.c @@ -355,28 +355,51 @@ alg_id_from_str(const str_t* alg, /*out*/ ALG_ID* alg_id) } static void -log_verify_error() +get_verify_error(char** verify_error) { + LOG_TRACE("get_verify_error enter"); + DWORD err = cp_function_list.GetLastError(); + const char* err_string; + switch (err) { case NTE_BAD_SIGNATURE: - LOG_WARN("sign is invalid: bad signature (0x%08x)", err); + err_string = "sign is invalid: bad signature (0x%08x)"; break; case NTE_BAD_ALGID: - LOG_WARN("sign is invalid: bad alg id (0x%08x)", err); + err_string = "sign is invalid: bad alg id (0x%08x)"; break; default: - LOG_WARN("sign is invalid. Last error code: 0x%08x", err); + err_string = "sign is invalid. Last error code: 0x%08x"; break; - } + } + + size_t size = strlen(err_string) + 4 /*error code*/ + 1 /*terminating null*/; + + *verify_error = malloc(size); + if (*verify_error == NULL) { + LOG_ERROR("get_verify_error failed. Could not allocate memory for error string " + "(%zd bytes)", size); + return; + } + + int n = snprintf(*verify_error, size, err_string, err); + + if (n < 0 || (size_t)n >= size) { + LOG_ERROR("get_verify_error failed. Error occurred in concatenation err_string"); + free(*verify_error); + return; + } + + LOG_TRACE("get_verify_error exit"); } int -cryptopro_verify(const str_t* cert_thumbprint, const str_t* alg, const str_t *data, - const str_t *sign, bool* is_verified) +cryptopro_verify(const str_t* cert_thumbprint, const str_t* alg, const str_t* data, + const str_t* sign, bool* is_verified, char** verify_error) { int rc = -1; HCERTSTORE hStoreHandle = NULL; @@ -441,7 +464,11 @@ cryptopro_verify(const str_t* cert_thumbprint, const str_t* alg, const str_t *da LOG_DEBUG("sign is valid"); *is_verified = true; } else { - log_verify_error(); + get_verify_error(verify_error); + if (*verify_error == NULL) { + goto exit; + } + LOG_WARN("%s", *verify_error); } rc = 0; diff --git a/src/utils/cryptopro.h b/src/utils/cryptopro.h index e7efdc5..b76dbf1 100644 --- a/src/utils/cryptopro.h +++ b/src/utils/cryptopro.h @@ -27,6 +27,6 @@ bool cryptopro_init(const char* cp_file); int cryptopro_sign(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t *sign); int cryptopro_verify(const str_t* cert_thumbprint, const str_t* alg, const str_t *data, - const str_t *sign, bool* is_verified); + const str_t *sign, bool* is_verified, char** verify_error); #endif // CRYPTOPRO_H_INCLUDED From bb8dac32b93d64bd923fa82eeafce5a0a190f938 Mon Sep 17 00:00:00 2001 From: alashkova Date: Wed, 16 Oct 2024 16:08:01 +0300 Subject: [PATCH 10/37] =?UTF-8?q?SUPPORT-8592.=20=D0=92=20nginx.conf=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20location=20/?= =?UTF-8?q?verify?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/nginx-docker.conf | 9 +++++++-- conf/nginx.conf | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/conf/nginx-docker.conf b/conf/nginx-docker.conf index 362e9b9..6c00803 100644 --- a/conf/nginx-docker.conf +++ b/conf/nginx-docker.conf @@ -3,7 +3,12 @@ server { server_name localhost; location = /sign { - fastcgi_pass ervu-sign-module:9009; - include fastcgi_params; + fastcgi_pass ervu-sign-module:9009; + include fastcgi_params; + } + + location = /verify { + fastcgi_pass ervu-sign-module:9009; + include fastcgi_params; } } diff --git a/conf/nginx.conf b/conf/nginx.conf index 3703968..eacc93a 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -39,5 +39,10 @@ http { fastcgi_pass localhost:9009; include fastcgi_params; } + + location = /verify { + fastcgi_pass localhost:9009; + include fastcgi_params; + } } } From 3e15adb26567d92e94256696e8f58fd190ae3f02 Mon Sep 17 00:00:00 2001 From: alashkova Date: Wed, 16 Oct 2024 16:14:34 +0300 Subject: [PATCH 11/37] =?UTF-8?q?SUPPORT-8592.=20=D0=9E=D0=B1=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20README=20=D0=B8=20=D0=98=D0=BD?= =?UTF-8?q?=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=86=D0=B8=D1=8F=20=D0=BF=D0=BE?= =?UTF-8?q?=20=D1=83=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 38 +++++++++++++++++++++++++++++++++++++- Инструкция по установке.md | 28 +++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c39f2d9..747c3ac 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ ## Краткое описание -Модуль предназначен для подписи данных. +В модуле реализованы следующие функции: +- подпись данных +- проверка подписи маркера доступа + +### Подпись данных Приложение принимает POST-запрос по протоколу FastCGI (Content-Type: text/plain). Подписывает строку, полученную в теле запроса. @@ -28,6 +32,35 @@ $ curl -v http://127.0.0.1:8080/sign -H "Content-Type: text/plain" -d "test" urlSafeBase64_of_signed_string_test ``` +### Проверка подписи маркера доступа + +Приложение принимает POST-запрос по протоколу FastCGI (Content-Type: text/plain). +Проверяет подпись маркера доступа, полученного в теле запроса. +В ответе возвращает один из следующих статус-кодов: +- 200 OK - подпись валидна +- 401 Unauthorized - подпись невалидна (в теле ответа возвращается код ошибки от криптопровайдера) +- 500 Internal Server Error - внутренняя ошибка сервера + +Пример выполнения запроса: +``` +$ curl -v http://127.0.0.1:8080/verify -H "Content-Type: text/plain" -d "some_valid_access_token" +* Trying 127.0.0.1:8080... +* Connected to 127.0.0.1 (127.0.0.1) port 8080 +> POST /sign HTTP/1.1 +> Host: 127.0.0.1:8080 +> User-Agent: curl/8.4.0 +> Accept: */* +> Content-Type: text/plain +> Content-Length: 4 +> +< HTTP/1.1 200 OK +< Server: nginx/1.24.0 +< Date: Wed, 16 Oct 2024 09:55:46 GMT +< Transfer-Encoding: chunked +< Connection: keep-alive +< +``` + ## Сборка из исходников Инструкции по сборке из исходников см. "Инструкция по сборке.md" @@ -60,3 +93,6 @@ location = /sign *\# значение по умолчанию: /sign, должн signer_subject = signer@example.ru *\# email, ИНН, СНИЛС или любая другая строка из свойства контейнера «Субъект»* pin = \*\*\*\* *\# пароль от контейнера* +- В секции **\[verify\]** задать настройки проверки подписи маркера доступа: +location = /verify *\# значение по умолчанию: /verify, должно совпадать со значением в nginx.conf* +esia_cert_thumbprint = sha1thumbprint *\# SHA1 отпечаток сертификата ЕСИА* diff --git a/Инструкция по установке.md b/Инструкция по установке.md index ffeb33e..3d78b7e 100644 --- a/Инструкция по установке.md +++ b/Инструкция по установке.md @@ -24,9 +24,11 @@ cd - /opt/cprocsp/sbin/amd64/cpconfig -license -set "Номер" ``` -4. Запросить у руководителя проекта реестра повесток контейнер с ключом ИС, зарегистрированной в ЕСИА. +### Установка ключевой пары ИС, зарегистрированной в ЕСИА -5. Установить полученную ключевую пару для пользователя, от которого запускается модуль подписания (команды выполняются от имени **ervu**). +1. Запросить у руководителя проекта реестра повесток контейнер с ключом ИС, зарегистрированной в ЕСИА. + +2. Установить полученную ключевую пару для пользователя, от которого запускается модуль подписания (команды выполняются от имени **ervu**). Внимание! В следующих командах необходимо заменить ***key_cont.000*** на название контейнера, полученного на предыдущем шаге. ``` bash @@ -38,22 +40,38 @@ chown -R ervu:ervu /var/opt/cprocsp/keys/ervu/key_cont.000/ /opt/cprocsp/bin/amd64/csptest -absorb -certs -autoprov ``` -6. Получить перечень сертификатов: +3. Получить перечень сертификатов: ``` bash sudo -u ervu /opt/cprocsp/bin/amd64/certmgr -list ``` Получить следующие значения: - Поле "Субъект": любая подстрока из этого поля является signer_subject (нужно будет указать в конфигурационном файле модуля подписания) -- Поле "Ссылка на ключ": убедиться, что значение равно "Есть" (иначе проверьте выполнение предыдущих шагов) +- Поле "Ссылка на ключ": убедиться, что значение равно "Есть" (иначе необходимо проверить выполнение предыдущих шагов) -7. Сделать тестовую подпись с помощью установленного контейнера и полученных значений \ и \: +4. (опционально) Сделать тестовую подпись с помощью установленного контейнера и полученных значений \ и \: ``` bash touch test.txt sudo -u ervu /opt/cprocsp/bin/amd64/cryptcp -signf -dn ./test.txt rm -f test.txt test.txt.sgn ``` +### Установка сертификата ЕСИА + +1. Скачать сертификаты тестовой и продуктивной сред ЕСИА, используемые для формирования электронных подписей ответов как поставщика, по ссылке: https://esia.gosuslugi.ru/public/esia.zip + +2. Из списка сертификатов выбрать тот, который подходит под конфигурацию стенда (в зависимости от алгоритма подписи и от того, является ли среда тестовой или продуктивной). + +3. Установить выбранный сертификат для пользователя, от которого запускается модуль подписания (команды выполняются от имени **ervu**). +``` bash +/opt/cprocsp/bin/amd64/certmgr -install -file '.cer' +``` + +Результатом команды будет список полей сертификата. Из него необходимо получить следующие значения: +- Поле "SHA1 отпечаток": это значение нужно будет указать в конфигурационном файле модуля подписания в качестве настройки \. +- Поле "Алгоритм подписи": проверить, что значение совпадает с настройками ИС в ЕСИА. +- Поле "Истекает": проверить, что срок действия сертификата не истёк. + ### Установка и настройка nginx Дальнейшие шаги выполняются от имени root: From 08c9a80bd00c95814684cbd43dc5f577076fc8e0 Mon Sep 17 00:00:00 2001 From: alashkova Date: Thu, 17 Oct 2024 16:25:24 +0300 Subject: [PATCH 12/37] =?UTF-8?q?SUPPORT-8592.=20=D0=9E=D1=87=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D0=BA=D0=B0=20=D0=BC=D0=B0=D0=BF=D1=8B=20=D1=81=20=D1=85?= =?UTF-8?q?=D1=8D=D0=BD=D0=B4=D0=BB=D0=B5=D1=80=D0=B0=D0=BC=D0=B8=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=B4=20=D0=BE=D1=81=D1=82=D0=B0=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=BA=D0=BE=D0=B9=20fcgi-=D1=81=D0=B5=D1=80=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/fcgisrv/fcgi_map.c | 11 +++++++++++ src/fcgisrv/fcgi_server.c | 8 ++++++++ src/fcgisrv/fcgi_server_internal.h | 1 + 3 files changed, 20 insertions(+) diff --git a/src/fcgisrv/fcgi_map.c b/src/fcgisrv/fcgi_map.c index fef04c5..827967c 100644 --- a/src/fcgisrv/fcgi_map.c +++ b/src/fcgisrv/fcgi_map.c @@ -101,3 +101,14 @@ fcgi_handler_map_get(fcgi_handler_map_t* map, char* path) return handler; } + + +void +fcgi_handler_map_clear(fcgi_handler_map_t* map) +{ + if (map == NULL) return; + + for (size_t i = 0; i < map->num; ++i) { + fcgi_map_item_clear(&map->locations[i]); + } +} \ No newline at end of file diff --git a/src/fcgisrv/fcgi_server.c b/src/fcgisrv/fcgi_server.c index f5a15d1..68e39cb 100644 --- a/src/fcgisrv/fcgi_server.c +++ b/src/fcgisrv/fcgi_server.c @@ -230,6 +230,13 @@ fcgi_register_handler(HFcgi hfcgi, char* path, fcgi_handler_t* handler) } +static void +fcgi_unregister_handlers(HFcgi hfcgi) +{ + fcgi_handler_map_clear(&hfcgi->handler_map); +} + + int fcgi_run_server(HFcgi hfcgi) { @@ -299,6 +306,7 @@ fcgi_dispose_server(HFcgi hfcgi) { LOG_TRACE("fcgi_dispose_server enter"); + fcgi_unregister_handlers(hfcgi); free(hfcgi); LOG_TRACE("fcgi_dispose_server exit"); diff --git a/src/fcgisrv/fcgi_server_internal.h b/src/fcgisrv/fcgi_server_internal.h index 227f110..930b46b 100644 --- a/src/fcgisrv/fcgi_server_internal.h +++ b/src/fcgisrv/fcgi_server_internal.h @@ -66,5 +66,6 @@ void fcgi_server_thread_pool_dispose(fcgi_thread_pool_t* thread_pool); int fcgi_handler_map_set(fcgi_handler_map_t* map, char* path, fcgi_handler_t* handler); fcgi_handler_t* fcgi_handler_map_get(fcgi_handler_map_t* map, char* path); +void fcgi_handler_map_clear(fcgi_handler_map_t* map); #endif // FCGI_SERVER_INTERNAL_H_INCLUDED From 21ba41e0dde672d1c4744b7dd280c45cf72fa58b Mon Sep 17 00:00:00 2001 From: alashkova Date: Thu, 17 Oct 2024 16:34:25 +0300 Subject: [PATCH 13/37] SUPPORT-8592. fcgi_verify_request_clear(): memset(0) added --- src/modules/service_verify.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/service_verify.c b/src/modules/service_verify.c index 63747c7..0a33554 100644 --- a/src/modules/service_verify.c +++ b/src/modules/service_verify.c @@ -236,6 +236,8 @@ fcgi_verify_request_clear(fcgi_verify_request_t *req_info) free(req_info->content); free(req_info->verify_error); + + memset(req_info, 0, sizeof(fcgi_verify_request_t)); } static int From 2b047d2155a11702eedd218d913ae7c71ffe4059 Mon Sep 17 00:00:00 2001 From: alashkova Date: Thu, 17 Oct 2024 16:40:02 +0300 Subject: [PATCH 14/37] SUPPORT-8592. decode_base64_url() check return value --- src/modules/service_verify.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/modules/service_verify.c b/src/modules/service_verify.c index 0a33554..b6d7a6b 100644 --- a/src/modules/service_verify.c +++ b/src/modules/service_verify.c @@ -335,7 +335,10 @@ verify_jwt_sign(fcgi_verify_request_t* req_info, const verify_service_t *ctx) LOG_ERROR("Could not allocate memory for decoded sign (%zd bytes)", sign.len + 1); goto error; } - decode_base64_url(&sign, &sign_base64); + if (decode_base64_url(&sign, &sign_base64)) { + LOG_ERROR("Could not decode jwt sign"); + goto error; + } header.len = base64_decoded_length(header_base64.len); header.data = calloc(1, header.len + 1); @@ -343,7 +346,10 @@ verify_jwt_sign(fcgi_verify_request_t* req_info, const verify_service_t *ctx) LOG_ERROR("Could not allocate memory for decoded header (%zd bytes)", header.len + 1); goto error; } - decode_base64_url(&header, &header_base64); + if (decode_base64_url(&header, &header_base64)) { + LOG_ERROR("Could not decode jwt header"); + goto error; + } if (get_alg_from_header(&header, &alg)) { goto error; From 987d2c2710c64c00ec1913bb7ce75b26efb7be89 Mon Sep 17 00:00:00 2001 From: alashkova Date: Thu, 17 Oct 2024 16:43:19 +0300 Subject: [PATCH 15/37] SUPPORT-8592. get_verify_error() fixed --- src/utils/cryptopro.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/cryptopro.c b/src/utils/cryptopro.c index 099fbd1..3192fcc 100644 --- a/src/utils/cryptopro.c +++ b/src/utils/cryptopro.c @@ -391,6 +391,7 @@ get_verify_error(char** verify_error) if (n < 0 || (size_t)n >= size) { LOG_ERROR("get_verify_error failed. Error occurred in concatenation err_string"); free(*verify_error); + *verify_error = NULL; return; } From 03dbccf87439d3ef6ea0a9dac82ad59f24cc634c Mon Sep 17 00:00:00 2001 From: alashkova Date: Thu, 17 Oct 2024 16:46:46 +0300 Subject: [PATCH 16/37] SUPPORT-8592. minor fix --- src/utils/str_t.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/str_t.c b/src/utils/str_t.c index 76bb317..5c3895b 100644 --- a/src/utils/str_t.c +++ b/src/utils/str_t.c @@ -94,6 +94,6 @@ hex_to_bin(const str_t* hex, /*out*/ str_t* bin) error: str_t_clear(bin); - LOG_ERROR("hex2bin exit with error"); + LOG_ERROR("hex_to_bin exit with error"); return -1; } \ No newline at end of file From 010fe49ddadf2cf49f2786488fbbec794eba041b Mon Sep 17 00:00:00 2001 From: alashkova Date: Fri, 18 Oct 2024 16:14:11 +0300 Subject: [PATCH 17/37] =?UTF-8?q?SUPPORT-8592.=20=D0=9F=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B2=20=D0=B8=D0=BD=D1=81=D1=82=D1=80=D1=83?= =?UTF-8?q?=D0=BA=D1=86=D0=B8=D0=B8=20=D0=BF=D0=BE=20=D1=83=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D0=BD=D0=BE=D0=B2=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Инструкция по установке.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Инструкция по установке.md b/Инструкция по установке.md index 3d78b7e..a91158f 100644 --- a/Инструкция по установке.md +++ b/Инструкция по установке.md @@ -42,7 +42,7 @@ chown -R ervu:ervu /var/opt/cprocsp/keys/ervu/key_cont.000/ 3. Получить перечень сертификатов: ``` bash -sudo -u ervu /opt/cprocsp/bin/amd64/certmgr -list +/opt/cprocsp/bin/amd64/certmgr -list ``` Получить следующие значения: @@ -52,7 +52,7 @@ sudo -u ervu /opt/cprocsp/bin/amd64/certmgr -list 4. (опционально) Сделать тестовую подпись с помощью установленного контейнера и полученных значений \ и \: ``` bash touch test.txt -sudo -u ervu /opt/cprocsp/bin/amd64/cryptcp -signf -dn ./test.txt +/opt/cprocsp/bin/amd64/cryptcp -signf -dn ./test.txt rm -f test.txt test.txt.sgn ``` From 1c99d4016c70e06a6970db807605af6e7976ca18 Mon Sep 17 00:00:00 2001 From: alashkova Date: Fri, 18 Oct 2024 16:27:41 +0300 Subject: [PATCH 18/37] =?UTF-8?q?SUPPORT-8592.=20=D0=A0=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=B0=20=D1=81=20jwt-=D1=82=D0=BE=D0=BA=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=D0=BC=D0=B8=20=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=B2=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D1=84=D0=B0=D0=B9=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 + src/modules/service_verify.c | 75 ++---------------------------------- src/utils/jwt.c | 75 ++++++++++++++++++++++++++++++++++++ src/utils/jwt.h | 13 +++++++ 4 files changed, 92 insertions(+), 72 deletions(-) create mode 100644 src/utils/jwt.c create mode 100644 src/utils/jwt.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c9fecbb..50d447c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,6 +112,7 @@ ADD_EXECUTABLE (${PROJECT_NAME} ${UTILS_DIR}/gconf_file.c ${UTILS_DIR}/glib_utils.c ${UTILS_DIR}/json_parser.c + ${UTILS_DIR}/jwt.c ${UTILS_DIR}/library.c ${UTILS_DIR}/logger.c ${UTILS_DIR}/str_t.c diff --git a/src/modules/service_verify.c b/src/modules/service_verify.c index b6d7a6b..fd82789 100644 --- a/src/modules/service_verify.c +++ b/src/modules/service_verify.c @@ -4,7 +4,7 @@ #include "utils/base64.h" #include "utils/cryptopro.h" -#include "utils/json_parser.h" +#include "utils/jwt.h" #define VERIFY_CONF_SECTION "verify" @@ -22,8 +22,6 @@ static const str_t VERIFY_CONF_DEFAULT_LOCATION = str_t_const("/verify"); static const int CLIENT_MAX_BODY_SIZE = 8192; static const char* ACCEPTABLE_CONTENT_TYPE = "text/plain"; -static const char* JWT_ALG_FIELD_NAME = "alg"; - typedef struct verify_service_s { const verify_conf_t *conf; @@ -240,73 +238,6 @@ fcgi_verify_request_clear(fcgi_verify_request_t *req_info) memset(req_info, 0, sizeof(fcgi_verify_request_t)); } -static int -get_jwt_header_payload_and_sign(const str_t *jwt, - /*out*/ str_t *header, - /*out*/ str_t *header_payload, - /*out*/ str_t *sign) -{ - LOG_TRACE("get_jwt_header_payload_and_sign enter"); - - size_t p_start = 0, p_end = 0; - - for (size_t i = 0; i < jwt->len; i++) { - if (jwt->data[i] == '.') { - if (p_start == 0) { - p_start = i + 1; - } else { - p_end = i; - break; - } - } - } - - if (p_start == 0 || p_end == 0) { - LOG_ERROR("Could not parse jwt. p_start = %zd, p_end = %zd", p_start, p_end); - return -1; - } - - header->data = (char*)jwt->data; - header->len = p_start - 1; - - header_payload->data = (char*)jwt->data; - header_payload->len = p_end; - - sign->data = (char*)jwt->data + p_end + 1; - sign->len = jwt->len - p_end - 1; - - LOG_DEBUG("header: %.*s", (int) header->len, header->data); - LOG_DEBUG("header_payload: %.*s", (int) header_payload->len, header_payload->data); - LOG_DEBUG("sign: %.*s", (int) sign->len, sign->data); - - LOG_TRACE("get_jwt_header_payload_and_sign exit"); - - return 0; -} - -static int -get_alg_from_header(const str_t *header, /*out*/ str_t *alg) -{ - LOG_TRACE("get_alg_from_header"); - - Hjson_parser parser = json_parser_create(header->data, header->len); - if (parser == NULL) { - goto error; - } - - if (json_get_string_member(parser, JWT_ALG_FIELD_NAME, alg)) { - goto error; - } - - json_parser_free(parser); - return 0; - -error: - json_parser_free(parser); - LOG_ERROR("get_alg_from_header exit with error"); - return -1; -} - static fcgi_handler_status_t verify_jwt_sign(fcgi_verify_request_t* req_info, const verify_service_t *ctx) { @@ -325,7 +256,7 @@ verify_jwt_sign(fcgi_verify_request_t* req_info, const verify_service_t *ctx) str_t header_payload = str_t_null; str_t alg = str_t_null; - if (get_jwt_header_payload_and_sign(&jwt, &header_base64, &header_payload, &sign_base64)) { + if (jwt_get_header_payload_and_sign(&jwt, &header_base64, &header_payload, &sign_base64)) { goto error; } @@ -351,7 +282,7 @@ verify_jwt_sign(fcgi_verify_request_t* req_info, const verify_service_t *ctx) goto error; } - if (get_alg_from_header(&header, &alg)) { + if (jwt_get_alg_from_header(&header, &alg)) { goto error; } diff --git a/src/utils/jwt.c b/src/utils/jwt.c new file mode 100644 index 0000000..91ae4b7 --- /dev/null +++ b/src/utils/jwt.c @@ -0,0 +1,75 @@ +#include "jwt.h" + +#include "json_parser.h" +#include "logger.h" + +#define JWT_ALG_FIELD_NAME "alg" + +int +jwt_get_header_payload_and_sign(const str_t *jwt, + /*out*/ str_t *header, + /*out*/ str_t *header_payload, + /*out*/ str_t *sign) +{ + LOG_TRACE("jwt_get_header_payload_and_sign enter"); + + size_t p_start = 0, p_end = 0; + + for (size_t i = 0; i < jwt->len; i++) { + if (jwt->data[i] == '.') { + if (p_start == 0) { + p_start = i + 1; + } else { + p_end = i; + break; + } + } + } + + if (p_start == 0 || p_end == 0) { + LOG_ERROR("Could not parse jwt. p_start = %zd, p_end = %zd", p_start, p_end); + return -1; + } + + header->data = (char*)jwt->data; + header->len = p_start - 1; + + header_payload->data = (char*)jwt->data; + header_payload->len = p_end; + + sign->data = (char*)jwt->data + p_end + 1; + sign->len = jwt->len - p_end - 1; + + LOG_DEBUG("header: %.*s", (int) header->len, header->data); + LOG_DEBUG("header_payload: %.*s", (int) header_payload->len, header_payload->data); + LOG_DEBUG("sign: %.*s", (int) sign->len, sign->data); + + LOG_TRACE("jwt_get_header_payload_and_sign exit"); + + return 0; +} + +int +jwt_get_alg_from_header(const str_t *header, /*out*/ str_t *alg) +{ + LOG_TRACE("jwt_get_alg_from_header enter"); + + Hjson_parser parser = json_parser_create(header->data, header->len); + if (parser == NULL) { + goto error; + } + + if (json_get_string_member(parser, JWT_ALG_FIELD_NAME, alg)) { + goto error; + } + + json_parser_free(parser); + + LOG_TRACE("jwt_get_alg_from_header exit"); + return 0; + +error: + json_parser_free(parser); + LOG_ERROR("jwt_get_alg_from_header exit with error"); + return -1; +} diff --git a/src/utils/jwt.h b/src/utils/jwt.h new file mode 100644 index 0000000..f268849 --- /dev/null +++ b/src/utils/jwt.h @@ -0,0 +1,13 @@ +#ifndef JWT_H_INCLUDED +#define JWT_H_INCLUDED + +#include "str_t.h" + +int jwt_get_header_payload_and_sign(const str_t *jwt, + /*out*/ str_t *header, + /*out*/ str_t *header_payload, + /*out*/ str_t *sign); + +int jwt_get_alg_from_header(const str_t *header, /*out*/ str_t *alg); + +#endif // JWT_H_INCLUDED \ No newline at end of file From f2383bf83d9597cac59d229f54910082ddd522d1 Mon Sep 17 00:00:00 2001 From: alashkova Date: Fri, 18 Oct 2024 16:32:24 +0300 Subject: [PATCH 19/37] =?UTF-8?q?SUPPORT-8592.=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=84=D1=83=D0=B3=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=B9=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D1=81=20jwt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 12 ++++++-- tests/CMakeLists.txt | 30 +++++++++++++++++++ tests/main.c | 30 +++++++++++++++++++ tests/utils/test_jwt.c | 68 ++++++++++++++++++++++++++++++++++++++++++ tests/utils/test_jwt.h | 8 +++++ 5 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/main.c create mode 100644 tests/utils/test_jwt.c create mode 100644 tests/utils/test_jwt.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 50d447c..c01c1dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,9 @@ CMAKE_MINIMUM_REQUIRED (VERSION 3.0) -PROJECT (ervu-sign-module VERSION 1.0.0 LANGUAGES C) - SET (CMAKE_C_COMPILER "gcc") +PROJECT (ervu-sign-module VERSION 1.0.0 LANGUAGES C) + IF (CMAKE_VERBOSE) SET (CMAKE_VERBOSE_MAKEFILE 1) ENDIF () @@ -98,6 +98,14 @@ INCLUDE_DIRECTORIES ("${CRYPTOPRO_INCLUDE_DIRS}") FILE (GLOB_RECURSE HEADERS "${SOURCE_DIR}/*.h") +# tests +option(WITH_TESTS "Build with tests" OFF) +IF (WITH_TESTS) + MESSAGE ("Build with tests: ON") + enable_testing() + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests/) +ENDIF () + ADD_EXECUTABLE (${PROJECT_NAME} ${HEADERS} ${SOURCE_DIR}/main.c diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..6351e3f --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,30 @@ +project (test-ervu-sign-module LANGUAGES C) + +include_directories ( + ../src + ${GLIB2_INCLUDE_DIRS} + ${JSONGLIB_INCLUDE_DIRS} +) + +set (ESM_SOURCES + ../src/utils/glib_utils.c + ../src/utils/json_parser.c + ../src/utils/jwt.c + ../src/utils/logger.c +) + +set (HEADERS + utils/test_jwt.h +) + +set (SOURCES + main.c + utils/test_jwt.c + "${ESM_SOURCES}" +) + +add_executable(test-ervu-sign-module ${HEADERS} ${SOURCES}) + +target_link_libraries(test-ervu-sign-module cunit glib-2.0 json-glib-1.0 gobject-2.0) + +add_test(NAME test-ervu-sign-module COMMAND test-ervu-sign-module) diff --git a/tests/main.c b/tests/main.c new file mode 100644 index 0000000..b17b044 --- /dev/null +++ b/tests/main.c @@ -0,0 +1,30 @@ +#include "utils/test_jwt.h" + +#include + +int main(int arv, char** argc) +{ + int failures = -1; + (void)arv; + (void)argc; + + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + CU_pSuite suite_jwt = CU_add_suite("JWTSuit", NULL, NULL); + if (NULL == suite_jwt) { + goto exit; + } + + if (NULL == CU_ADD_TEST(suite_jwt, test_jwt_get_header_payload_and_sign)) goto exit; + if (NULL == CU_ADD_TEST(suite_jwt, test_jwt_get_alg_from_header)) goto exit; + + CU_basic_set_mode(CU_BRM_NORMAL); + CU_basic_run_tests(); + +exit: + failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + + return (failures == 0) ? 0 : -1; +} diff --git a/tests/utils/test_jwt.c b/tests/utils/test_jwt.c new file mode 100644 index 0000000..eb27dd9 --- /dev/null +++ b/tests/utils/test_jwt.c @@ -0,0 +1,68 @@ +#include "test_jwt.h" +#include "utils/jwt.h" + +#include + +#define JWT "eyJ2ZXIiOjEsInR5cCI6IkpXVCIsInNidCI6ImF1dGhvcml6YXRpb25fY29kZSIsImFsZyI6IkdP" \ + "U1QzNDEwXzIwMTJfMjU2In0.eyJuYmYiOjE2ODE3MzI4ODQsInNjb3BlIjoib3BlbmlkIiwiYXV0" \ + "aF90aW1lIjoxNjgxNzMyODc2LCJpc3MiOiJodHRwOlwvXC9lc2lhLXBvcnRhbDEudGVzdC5nb3N1" \ + "c2x1Z2kucnVcLyIsInVybjplc2lhOnNpZCI6Ijg3NWQzNDZhLTgzMTktMmM1MS05ZDYwLTYwZmRk" \ + "MzE4MGVhOSIsInVybjplc2lhOmNsaWVudDpzdGF0ZSI6Ijg5MzgzNDdhLTI0MmMtNGVhYy05NTdk" \ + "LTk4MDgzNTAzYzUwNyIsImF1dGhfbXRoZCI6IlBXRCIsInVybjplc2lhOnNiaiI6eyJ1cm46ZXNp" \ + "YTpzYmo6bHZsIjoiUCIsInVybjplc2lhOnNiajp0eXAiOiJQIiwidXJuOmVzaWE6c2JqOm9pZCI6" \ + "MTAwMDQwMDgyMywidXJuOmVzaWE6c2JqOm5hbSI6Ik9JRC4xMDAwNDAwODIzIn0sImV4cCI6MTY4" \ + "MTczMzEyNCwicGFyYW1zIjp7fSwiaWF0IjoxNjgxNzMyODg0LCJjbGllbnRfaWQiOiI1MDAyMDEi" \ + "fQ.8EBVu5Cizok66n1sIrVmS07N_4j9TuaKHlQffr98hwv9amUN5zpckH4dyJtO6IA-8UkhZ19ww" \ + "Of0F_VCmhOqmQ" + +#define JWT_HEADER "eyJ2ZXIiOjEsInR5cCI6IkpXVCIsInNidCI6ImF1dGhvcml6YXRpb25fY29kZSIsImFsZyI6IkdP" \ + "U1QzNDEwXzIwMTJfMjU2In0" + +#define JWT_HEADER_PAYLOAD "eyJ2ZXIiOjEsInR5cCI6IkpXVCIsInNidCI6ImF1dGhvcml6YXRpb25fY29kZSIsImFsZ" \ + "yI6IkdPU1QzNDEwXzIwMTJfMjU2In0.eyJuYmYiOjE2ODE3MzI4ODQsInNjb3BlIjoib3" \ + "BlbmlkIiwiYXV0aF90aW1lIjoxNjgxNzMyODc2LCJpc3MiOiJodHRwOlwvXC9lc2lhLXB" \ + "vcnRhbDEudGVzdC5nb3N1c2x1Z2kucnVcLyIsInVybjplc2lhOnNpZCI6Ijg3NWQzNDZh" \ + "LTgzMTktMmM1MS05ZDYwLTYwZmRkMzE4MGVhOSIsInVybjplc2lhOmNsaWVudDpzdGF0Z" \ + "SI6Ijg5MzgzNDdhLTI0MmMtNGVhYy05NTdkLTk4MDgzNTAzYzUwNyIsImF1dGhfbXRoZC" \ + "I6IlBXRCIsInVybjplc2lhOnNiaiI6eyJ1cm46ZXNpYTpzYmo6bHZsIjoiUCIsInVybjp" \ + "lc2lhOnNiajp0eXAiOiJQIiwidXJuOmVzaWE6c2JqOm9pZCI6MTAwMDQwMDgyMywidXJu" \ + "OmVzaWE6c2JqOm5hbSI6Ik9JRC4xMDAwNDAwODIzIn0sImV4cCI6MTY4MTczMzEyNCwic" \ + "GFyYW1zIjp7fSwiaWF0IjoxNjgxNzMyODg0LCJjbGllbnRfaWQiOiI1MDAyMDEifQ" + +#define JWT_SIGN "8EBVu5Cizok66n1sIrVmS07N_4j9TuaKHlQffr98hwv9amUN5zpckH4dyJtO6IA-8UkhZ19ww" \ + "Of0F_VCmhOqmQ" + +#define JWT_HEADER_DECODED "{\"ver\":1,\"typ\":\"JWT\",\"sbt\":\"authorization_code\"," \ + "\"alg\":\"GOST3410_2012_256\"}" + +#define JWT_ALG "GOST3410_2012_256" + +void test_jwt_get_header_payload_and_sign() +{ + int rc; + str_t jwt = str_t_const(JWT); + str_t sign = str_t_null; + str_t header = str_t_null; + str_t header_payload = str_t_null; + + rc = jwt_get_header_payload_and_sign(&jwt, &header, &header_payload, &sign); + CU_ASSERT_EQUAL(rc, 0); + + CU_ASSERT_NSTRING_EQUAL(header.data, JWT_HEADER, header.len); + CU_ASSERT_NSTRING_EQUAL(header_payload.data, JWT_HEADER_PAYLOAD, header_payload.len); + CU_ASSERT_NSTRING_EQUAL(sign.data, JWT_SIGN, sign.len); +} + +void test_jwt_get_alg_from_header() +{ + int rc; + str_t header = str_t_const(JWT_HEADER_DECODED); + str_t alg = str_t_null; + + rc = jwt_get_alg_from_header(&header, &alg); + CU_ASSERT_EQUAL(rc, 0); + + CU_ASSERT_NSTRING_EQUAL(alg.data, JWT_ALG, alg.len); + + str_t_clear(&alg); +} \ No newline at end of file diff --git a/tests/utils/test_jwt.h b/tests/utils/test_jwt.h new file mode 100644 index 0000000..979e370 --- /dev/null +++ b/tests/utils/test_jwt.h @@ -0,0 +1,8 @@ +#ifndef TEST_JWT_H_INCLUDED +#define TEST_JWT_H_INCLUDED + +void test_jwt_get_header_payload_and_sign(); + +void test_jwt_get_alg_from_header(); + +#endif // TEST_JWT_H_INCLUDED \ No newline at end of file From 2bb0f43468dd6585ccf23c1c24e78ca55a47f8f4 Mon Sep 17 00:00:00 2001 From: alashkova Date: Fri, 18 Oct 2024 16:33:01 +0300 Subject: [PATCH 20/37] =?UTF-8?q?SUPPORT-8592.=20=D0=9F=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=BF=D0=BE=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/cryptopro.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/utils/cryptopro.c b/src/utils/cryptopro.c index 3192fcc..1a8b853 100644 --- a/src/utils/cryptopro.c +++ b/src/utils/cryptopro.c @@ -299,7 +299,7 @@ get_cert_by_thumbprint(HCERTSTORE hStoreHandle, const str_t* thumbprint) { str_t thumbprint_bin = str_t_null; - LOG_TRACE("get_cert_by_thumbprint"); + LOG_TRACE("get_cert_by_thumbprint enter"); if (hex_to_bin(thumbprint, &thumbprint_bin)) { goto error; @@ -323,6 +323,8 @@ get_cert_by_thumbprint(HCERTSTORE hStoreHandle, const str_t* thumbprint) str_t_clear(&thumbprint_bin); + LOG_TRACE("get_cert_by_thumbprint exit"); + return cert; error: @@ -334,24 +336,22 @@ error: static int alg_id_from_str(const str_t* alg, /*out*/ ALG_ID* alg_id) { - LOG_TRACE("alg_id_from_str"); + LOG_TRACE("alg_id_from_str enter"); if (strncmp(alg->data, "GOST3410_2012_256", alg->len) == 0) { *alg_id = CALG_GR3411_2012_256; - return 0; - } - if (strncmp(alg->data, "GOST3410_2012_512", alg->len) == 0) { + } else if (strncmp(alg->data, "GOST3410_2012_512", alg->len) == 0) { *alg_id = CALG_GR3411_2012_512; - return 0; - } - if (strncmp(alg->data, "RS256", alg->len) == 0) { + } else if (strncmp(alg->data, "RS256", alg->len) == 0) { *alg_id = CALG_SHA_256; - return 0; + } else { + LOG_ERROR("Unknown alg: '%.*s'", (int) alg->len, alg->data); + LOG_ERROR("alg_id_from_str exit with error"); + return -1; } - LOG_ERROR("Unknown alg: '%.*s'", (int) alg->len, alg->data); - LOG_ERROR("alg_id_from_str exit with error"); - return -1; + LOG_TRACE("alg_id_from_str exit"); + return 0; } static void From 489c07dd44cfe586ad22d10248d1aba8f430c139 Mon Sep 17 00:00:00 2001 From: alashkova Date: Fri, 18 Oct 2024 17:37:36 +0300 Subject: [PATCH 21/37] SUPPORT-8592. review fix --- src/fcgisrv/fcgi_utils.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/fcgisrv/fcgi_utils.c b/src/fcgisrv/fcgi_utils.c index 5c60f6d..9066d7f 100644 --- a/src/fcgisrv/fcgi_utils.c +++ b/src/fcgisrv/fcgi_utils.c @@ -37,14 +37,11 @@ check_content_length(int content_length, int client_max_body_size) return HANDLER_HTTP_BAD_REQUEST; } - if (content_length > client_max_body_size) { - LOG_WARN("Content-Length (%d) is too large (> %d)", - content_length, - client_max_body_size); - return HANDLER_HTTP_REQUEST_ENTITY_TOO_LARGE; - } - - return HANDLER_ERROR; + /* (content_length > client_max_body_size) */ + LOG_WARN("Content-Length (%d) is too large (> %d)", + content_length, + client_max_body_size); + return HANDLER_HTTP_REQUEST_ENTITY_TOO_LARGE; } From 4c1e5c356280887265bfa4216fe45e2f0aa5a907 Mon Sep 17 00:00:00 2001 From: alashkova Date: Tue, 22 Oct 2024 11:12:38 +0300 Subject: [PATCH 22/37] =?UTF-8?q?SUPPORT-8592.=20=D0=92=20=D0=B8=D0=BD?= =?UTF-8?q?=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=86=D0=B8=D1=8E=20=D0=BF=D0=BE?= =?UTF-8?q?=20=D1=83=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BA=D0=B5=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D1=81=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=BE=D0=BA=20=D1=81=D0=B5=D1=80=D1=82=D0=B8=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D0=B0=D1=82=D0=BE=D0=B2=20=D0=95=D0=A1=D0=98=D0=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Инструкция по установке.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Инструкция по установке.md b/Инструкция по установке.md index a91158f..2c21942 100644 --- a/Инструкция по установке.md +++ b/Инструкция по установке.md @@ -60,7 +60,17 @@ rm -f test.txt test.txt.sgn 1. Скачать сертификаты тестовой и продуктивной сред ЕСИА, используемые для формирования электронных подписей ответов как поставщика, по ссылке: https://esia.gosuslugi.ru/public/esia.zip -2. Из списка сертификатов выбрать тот, который подходит под конфигурацию стенда (в зависимости от алгоритма подписи и от того, является ли среда тестовой или продуктивной). +2. Из списка сертификатов выбрать тот, который подходит под конфигурацию стенда (в зависимости от алгоритма подписи и от того, является ли среда тестовой или продуктивной): + +| Среда \ Алгоритм подписания | RS256 | GOST3410_2012_256 | +|-----------------------------|-----------------------------------------------------|-----------------------------------------------------| +| | RSA_TESIA_2024.cer | TESIA GOST 2012 new.cer | +| Тестовая | Отпечаток: 6a3813240f6cc285e6d4e6d9a4bfeb26038195d8 | Отпечаток: e654f6114dc19bae84c5748794a4fd7797726e71 | +| | Срок действия: по 06.08.2034 | Срок действия: по 24.04.2025 | +|-----------------------------|-----------------------------------------------------|-----------------------------------------------------| +| | RSA_PROD_2024.cer | ГОСТ+ПРОД+24-25.cer | +| Продуктивная | Отпечаток: 29c2f9c8e064d73509eadd685a5508712735e303 | Отпечаток: 20ad21785b9161ef46f075d4dc23c34e1e349228 | +| | Срок действия: по 12.05.2025 | Срок действия: по 08.06.2025 | 3. Установить выбранный сертификат для пользователя, от которого запускается модуль подписания (команды выполняются от имени **ervu**). ``` bash From 59005fe670b4e97761e33df28ae2d6de1cacf408 Mon Sep 17 00:00:00 2001 From: alashkova Date: Tue, 22 Oct 2024 11:31:34 +0300 Subject: [PATCH 23/37] =?UTF-8?q?SUPPORT-8592.=20=D0=9F=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B8=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=D0=BD=D0=B0=20=D0=BD?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9=D0=BA=D0=B0=20sign::pin=20?= =?UTF-8?q?=D0=B2=20sign::sign=5Fcert=5Fpassword?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- conf/ervu-sign-module.conf.example | 2 +- src/modules/service_sign.c | 14 ++++++------ src/modules/service_sign.h | 2 +- src/utils/cryptopro.c | 34 ++++++++++++++---------------- src/utils/cryptopro.h | 6 +++--- Инструкция по установке.md | 2 +- 7 files changed, 30 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 747c3ac..29b24ba 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ fcgi_thread_pool_size = 1 *\# значение по умолчанию: 1* - В секции **\[sign\]** задать настройки модуля подписания: location = /sign *\# значение по умолчанию: /sign, должно совпадать со значением в nginx.conf* signer_subject = signer@example.ru *\# email, ИНН, СНИЛС или любая другая строка из свойства контейнера «Субъект»* -pin = \*\*\*\* *\# пароль от контейнера* +sign_cert_password = \*\*\*\* *\# пароль от контейнера* - В секции **\[verify\]** задать настройки проверки подписи маркера доступа: location = /verify *\# значение по умолчанию: /verify, должно совпадать со значением в nginx.conf* diff --git a/conf/ervu-sign-module.conf.example b/conf/ervu-sign-module.conf.example index f50d665..50da565 100644 --- a/conf/ervu-sign-module.conf.example +++ b/conf/ervu-sign-module.conf.example @@ -10,7 +10,7 @@ fcgi_listen_host = 127.0.0.1 [sign] #location = /sign signer_subject = signer@example.ru -pin = **** +sign_cert_password = **** [verify] #location = /verify diff --git a/src/modules/service_sign.c b/src/modules/service_sign.c index 429c142..d0cc461 100644 --- a/src/modules/service_sign.c +++ b/src/modules/service_sign.c @@ -7,7 +7,7 @@ #define SIGN_CONF_SECTION "sign" #define SIGN_CONF_KEY_LOCATION "location" #define SIGN_CONF_KEY_SIGNER "signer_subject" -#define SIGN_CONF_KEY_PIN "pin" +#define SIGN_CONF_KEY_PASSWORD "sign_cert_password" #define FCGI_OK_RESPONSE_FORMAT \ "Content-type: text/plain" CRLF\ @@ -66,8 +66,8 @@ sign_conf_load(sign_conf_t *conf, const conf_file_context_t conf_file) }, { SIGN_CONF_SECTION, - SIGN_CONF_KEY_PIN, - &(conf->pin), + SIGN_CONF_KEY_PASSWORD, + &(conf->sign_cert_password), CONF_FILE_VALUE_STRT, CONF_FILE_VALUE_PSWD, NULL @@ -94,10 +94,10 @@ sign_conf_clear(sign_conf_t *conf) if (conf == NULL) return; - if (conf->pin.data != 0) { - explicit_bzero(conf->pin.data, conf->pin.len); + if (conf->sign_cert_password.data != 0) { + explicit_bzero(conf->sign_cert_password.data, conf->sign_cert_password.len); } - str_t_clear(&conf->pin); + str_t_clear(&conf->sign_cert_password); str_t_clear(&conf->location); free(conf->signer); @@ -118,7 +118,7 @@ sign_service_create(const sign_conf_t *conf) hsign->conf = conf; - cryptopro_context_set(&hsign->cryptopro_ctx, conf->signer, &conf->pin); + cryptopro_context_set(&hsign->cryptopro_ctx, conf->signer, &conf->sign_cert_password); LOG_TRACE("sign_service_create exit"); return (HSign)hsign; diff --git a/src/modules/service_sign.h b/src/modules/service_sign.h index 8289199..5bc4546 100644 --- a/src/modules/service_sign.h +++ b/src/modules/service_sign.h @@ -12,7 +12,7 @@ typedef struct sign_conf_s { str_t location; char *signer; /* субъект контейнера */ - str_t pin; /* pin-код от контейнера */ + str_t sign_cert_password; /* пароль от контейнера */ } sign_conf_t; diff --git a/src/utils/cryptopro.c b/src/utils/cryptopro.c index 1a8b853..fc35ca2 100644 --- a/src/utils/cryptopro.c +++ b/src/utils/cryptopro.c @@ -16,7 +16,7 @@ static HCERTSTORE cert_open_store(); static PCCERT_CONTEXT get_signer_cert(const cryptopro_context_t *ctx, HCERTSTORE hStoreHandle); -static int set_pin(PCCERT_CONTEXT pSignerCert, const str_t *pin); +static int set_password(PCCERT_CONTEXT cert_ctx, const str_t *password); bool cryptopro_init(const char* cp_file) @@ -45,7 +45,7 @@ cryptopro_sign(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t assert(ctx != NULL); assert(ctx->signer != NULL); - assert(ctx->pin != NULL); + assert(ctx->password != NULL); assert(data != NULL && !str_t_is_null(*data)); assert(sign != NULL); @@ -211,45 +211,43 @@ cert_open_store() static PCCERT_CONTEXT get_signer_cert(const cryptopro_context_t *ctx, HCERTSTORE hStoreHandle) { - PCCERT_CONTEXT pSignerCert; - - pSignerCert = cp_function_list.CertFindCertificateInStore(hStoreHandle, + PCCERT_CONTEXT cert_ctx = cp_function_list.CertFindCertificateInStore(hStoreHandle, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR, ctx->signer, NULL); - if (pSignerCert == NULL) { + if (cert_ctx == NULL) { LOG_ERROR("Could not find certificate in store"); goto error; } - if (set_pin(pSignerCert, ctx->pin)) { + if (set_password(cert_ctx, ctx->password)) { goto error; } LOG_DEBUG("The signer's certificate was found"); - return pSignerCert; + return cert_ctx; error: - if (pSignerCert) { - cp_function_list.CertFreeCertificateContext(pSignerCert); + if (cert_ctx) { + cp_function_list.CertFreeCertificateContext(cert_ctx); } LOG_ERROR("get_signer_cert exit with error"); return NULL; } static int -set_pin(PCCERT_CONTEXT pSignerCert, const str_t *pin) +set_password(PCCERT_CONTEXT cert_ctx, const str_t *password) { DWORD dwSize; if (!cp_function_list.CertGetCertificateContextProperty( - pSignerCert, + cert_ctx, CERT_KEY_PROV_INFO_PROP_ID, NULL, &dwSize)) { LOG_ERROR("Error getting key property"); - LOG_ERROR("set_pin exit with error. Last error code: 0x%08x", cp_function_list.GetLastError()); + LOG_ERROR("set_password exit with error. Last error code: 0x%08x", cp_function_list.GetLastError()); return -1; } @@ -257,7 +255,7 @@ set_pin(PCCERT_CONTEXT pSignerCert, const str_t *pin) CRYPT_KEY_PROV_INFO* pKeyInfo = (CRYPT_KEY_PROV_INFO*) pKeyInfoBuffer; if (!cp_function_list.CertGetCertificateContextProperty( - pSignerCert, + cert_ctx, CERT_KEY_PROV_INFO_PROP_ID, pKeyInfo, &dwSize)){ @@ -272,14 +270,14 @@ set_pin(PCCERT_CONTEXT pSignerCert, const str_t *pin) memset(&keyProvParam, 0, sizeof(CRYPT_KEY_PROV_PARAM)); keyProvParam.dwFlags = 0; keyProvParam.dwParam = keyType; - keyProvParam.cbData = (DWORD)(pin->len + 1); - keyProvParam.pbData = (BYTE*) pin->data; + keyProvParam.cbData = (DWORD)(password->len + 1); + keyProvParam.pbData = (BYTE*) password->data; pKeyInfo->cProvParam = 1; pKeyInfo->rgProvParam = &keyProvParam; if (!cp_function_list.CertSetCertificateContextProperty( - pSignerCert, + cert_ctx, CERT_KEY_PROV_INFO_PROP_ID, 0, pKeyInfo)) { @@ -290,7 +288,7 @@ set_pin(PCCERT_CONTEXT pSignerCert, const str_t *pin) return 0; error: - LOG_ERROR("set_pin exit with error. Last error code: 0x%08x", cp_function_list.GetLastError()); + LOG_ERROR("set_password exit with error. Last error code: 0x%08x", cp_function_list.GetLastError()); return -1; } diff --git a/src/utils/cryptopro.h b/src/utils/cryptopro.h index b76dbf1..1f30c85 100644 --- a/src/utils/cryptopro.h +++ b/src/utils/cryptopro.h @@ -9,17 +9,17 @@ typedef struct cryptopro_context_s { const char *signer; - const str_t *pin; + const str_t *password; } cryptopro_context_t; static inline void -cryptopro_context_set(cryptopro_context_t *ctx, const char *signer, const str_t *pin) +cryptopro_context_set(cryptopro_context_t *ctx, const char *signer, const str_t *password) { assert(ctx != NULL); ctx->signer = signer; - ctx->pin = pin; + ctx->password = password; } bool cryptopro_init(const char* cp_file); diff --git a/Инструкция по установке.md b/Инструкция по установке.md index 2c21942..160acf0 100644 --- a/Инструкция по установке.md +++ b/Инструкция по установке.md @@ -49,7 +49,7 @@ chown -R ervu:ervu /var/opt/cprocsp/keys/ervu/key_cont.000/ - Поле "Субъект": любая подстрока из этого поля является signer_subject (нужно будет указать в конфигурационном файле модуля подписания) - Поле "Ссылка на ключ": убедиться, что значение равно "Есть" (иначе необходимо проверить выполнение предыдущих шагов) -4. (опционально) Сделать тестовую подпись с помощью установленного контейнера и полученных значений \ и \: +4. (опционально) Сделать тестовую подпись с помощью установленного контейнера и полученных значений \ и \: ``` bash touch test.txt /opt/cprocsp/bin/amd64/cryptcp -signf -dn ./test.txt From 5a4ee2cb69abaa4ae39178bf8c68351510c3d0b4 Mon Sep 17 00:00:00 2001 From: alashkova Date: Tue, 22 Oct 2024 15:22:21 +0300 Subject: [PATCH 24/37] =?UTF-8?q?SUPPORT-8592.=20=D0=9D=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=B9=D0=BA=D0=B0=20sign::signer=5Fsubject=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=B8=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D0=BD=D0=B0=20=D0=B2=20sign::sign=5Fcert=5Fthumbprint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- conf/ervu-sign-module.conf.example | 2 +- src/modules/service_sign.c | 22 ++++---- src/modules/service_sign.h | 2 +- src/utils/cryptopro.c | 84 ++++++++++++++---------------- src/utils/cryptopro.h | 6 +-- Инструкция по установке.md | 6 +-- 7 files changed, 61 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 29b24ba..8b6e752 100644 --- a/README.md +++ b/README.md @@ -90,9 +90,9 @@ fcgi_thread_pool_size = 1 *\# значение по умолчанию: 1* - В секции **\[sign\]** задать настройки модуля подписания: location = /sign *\# значение по умолчанию: /sign, должно совпадать со значением в nginx.conf* -signer_subject = signer@example.ru *\# email, ИНН, СНИЛС или любая другая строка из свойства контейнера «Субъект»* +sign_cert_thumbprint = sha1_thumbprint_of_signer_cert *\# SHA1 отпечаток сертификата, которым ИС подписывает секрет* sign_cert_password = \*\*\*\* *\# пароль от контейнера* - В секции **\[verify\]** задать настройки проверки подписи маркера доступа: location = /verify *\# значение по умолчанию: /verify, должно совпадать со значением в nginx.conf* -esia_cert_thumbprint = sha1thumbprint *\# SHA1 отпечаток сертификата ЕСИА* +esia_cert_thumbprint = sha1_thumbprint_of_esia_cert *\# SHA1 отпечаток сертификата ЕСИА* diff --git a/conf/ervu-sign-module.conf.example b/conf/ervu-sign-module.conf.example index 50da565..5d32295 100644 --- a/conf/ervu-sign-module.conf.example +++ b/conf/ervu-sign-module.conf.example @@ -9,7 +9,7 @@ fcgi_listen_host = 127.0.0.1 [sign] #location = /sign -signer_subject = signer@example.ru +sign_cert_thumbprint = sign_cert_password = **** [verify] diff --git a/src/modules/service_sign.c b/src/modules/service_sign.c index d0cc461..af94665 100644 --- a/src/modules/service_sign.c +++ b/src/modules/service_sign.c @@ -4,10 +4,10 @@ #include "utils/cryptopro.h" -#define SIGN_CONF_SECTION "sign" -#define SIGN_CONF_KEY_LOCATION "location" -#define SIGN_CONF_KEY_SIGNER "signer_subject" -#define SIGN_CONF_KEY_PASSWORD "sign_cert_password" +#define SIGN_CONF_SECTION "sign" +#define SIGN_CONF_KEY_LOCATION "location" +#define SIGN_CONF_KEY_SIGN_CERT_THUMBPRINT "sign_cert_thumbprint" +#define SIGN_CONF_KEY_SIGN_CERT_PASSWORD "sign_cert_password" #define FCGI_OK_RESPONSE_FORMAT \ "Content-type: text/plain" CRLF\ @@ -58,15 +58,15 @@ sign_conf_load(sign_conf_t *conf, const conf_file_context_t conf_file) }, { SIGN_CONF_SECTION, - SIGN_CONF_KEY_SIGNER, - &(conf->signer), - CONF_FILE_VALUE_STRING, + SIGN_CONF_KEY_SIGN_CERT_THUMBPRINT, + &(conf->sign_cert_thumbprint), + CONF_FILE_VALUE_STRT, CONF_FILE_VALUE_NONE, NULL }, { SIGN_CONF_SECTION, - SIGN_CONF_KEY_PASSWORD, + SIGN_CONF_KEY_SIGN_CERT_PASSWORD, &(conf->sign_cert_password), CONF_FILE_VALUE_STRT, CONF_FILE_VALUE_PSWD, @@ -100,7 +100,7 @@ sign_conf_clear(sign_conf_t *conf) str_t_clear(&conf->sign_cert_password); str_t_clear(&conf->location); - free(conf->signer); + str_t_clear(&conf->sign_cert_thumbprint); memset(conf, 0, sizeof(sign_conf_t)); } @@ -118,7 +118,9 @@ sign_service_create(const sign_conf_t *conf) hsign->conf = conf; - cryptopro_context_set(&hsign->cryptopro_ctx, conf->signer, &conf->sign_cert_password); + cryptopro_context_set(&hsign->cryptopro_ctx, + &conf->sign_cert_thumbprint, + &conf->sign_cert_password); LOG_TRACE("sign_service_create exit"); return (HSign)hsign; diff --git a/src/modules/service_sign.h b/src/modules/service_sign.h index 5bc4546..3bbd02c 100644 --- a/src/modules/service_sign.h +++ b/src/modules/service_sign.h @@ -11,7 +11,7 @@ typedef struct sign_service_t* HSign; typedef struct sign_conf_s { str_t location; - char *signer; /* субъект контейнера */ + str_t sign_cert_thumbprint; /* SHA1 отпечаток сертификата ИС */ str_t sign_cert_password; /* пароль от контейнера */ } sign_conf_t; diff --git a/src/utils/cryptopro.c b/src/utils/cryptopro.c index fc35ca2..b93b91f 100644 --- a/src/utils/cryptopro.c +++ b/src/utils/cryptopro.c @@ -44,7 +44,7 @@ cryptopro_sign(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t LOG_TRACE("cryptopro_sign enter"); assert(ctx != NULL); - assert(ctx->signer != NULL); + assert(!str_t_is_null(*ctx->cert_thumbprint)); assert(ctx->password != NULL); assert(data != NULL && !str_t_is_null(*data)); assert(sign != NULL); @@ -209,19 +209,52 @@ cert_open_store() } static PCCERT_CONTEXT -get_signer_cert(const cryptopro_context_t *ctx, HCERTSTORE hStoreHandle) +get_cert_by_thumbprint(HCERTSTORE hStoreHandle, const str_t* thumbprint) { - PCCERT_CONTEXT cert_ctx = cp_function_list.CertFindCertificateInStore(hStoreHandle, + str_t thumbprint_bin = str_t_null; + + LOG_TRACE("get_cert_by_thumbprint enter"); + + if (hex_to_bin(thumbprint, &thumbprint_bin)) { + goto error; + } + + CRYPT_HASH_BLOB hash = { + .pbData = (BYTE*)thumbprint_bin.data, + .cbData = thumbprint_bin.len + }; + + PCCERT_CONTEXT cert = cp_function_list.CertFindCertificateInStore(hStoreHandle, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, - CERT_FIND_SUBJECT_STR, - ctx->signer, + CERT_FIND_HASH, + &hash, NULL); - if (cert_ctx == NULL) { + if (cert == NULL) { LOG_ERROR("Could not find certificate in store"); goto error; } + str_t_clear(&thumbprint_bin); + + LOG_TRACE("get_cert_by_thumbprint exit"); + + return cert; + +error: + str_t_clear(&thumbprint_bin); + LOG_ERROR("get_cert_by_thumbprint exit with error"); + return NULL; +} + +static PCCERT_CONTEXT +get_signer_cert(const cryptopro_context_t *ctx, HCERTSTORE hStoreHandle) +{ + PCCERT_CONTEXT cert_ctx = get_cert_by_thumbprint(hStoreHandle, ctx->cert_thumbprint); + if (cert_ctx == NULL) { + goto error; + } + if (set_password(cert_ctx, ctx->password)) { goto error; } @@ -292,45 +325,6 @@ error: return -1; } -static PCCERT_CONTEXT -get_cert_by_thumbprint(HCERTSTORE hStoreHandle, const str_t* thumbprint) -{ - str_t thumbprint_bin = str_t_null; - - LOG_TRACE("get_cert_by_thumbprint enter"); - - if (hex_to_bin(thumbprint, &thumbprint_bin)) { - goto error; - } - - CRYPT_HASH_BLOB hash = { - .pbData = (BYTE*)thumbprint_bin.data, - .cbData = thumbprint_bin.len - }; - - PCCERT_CONTEXT cert = cp_function_list.CertFindCertificateInStore(hStoreHandle, - X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - 0, - CERT_FIND_HASH, - &hash, - NULL); - if (cert == NULL) { - LOG_ERROR("Could not find certificate in store"); - goto error; - } - - str_t_clear(&thumbprint_bin); - - LOG_TRACE("get_cert_by_thumbprint exit"); - - return cert; - -error: - str_t_clear(&thumbprint_bin); - LOG_ERROR("get_cert_by_thumbprint exit with error"); - return NULL; -} - static int alg_id_from_str(const str_t* alg, /*out*/ ALG_ID* alg_id) { diff --git a/src/utils/cryptopro.h b/src/utils/cryptopro.h index 1f30c85..cd3e1d8 100644 --- a/src/utils/cryptopro.h +++ b/src/utils/cryptopro.h @@ -8,17 +8,17 @@ typedef struct cryptopro_context_s { - const char *signer; + const str_t *cert_thumbprint; const str_t *password; } cryptopro_context_t; static inline void -cryptopro_context_set(cryptopro_context_t *ctx, const char *signer, const str_t *password) +cryptopro_context_set(cryptopro_context_t *ctx, const str_t *cert_thumbprint, const str_t *password) { assert(ctx != NULL); - ctx->signer = signer; + ctx->cert_thumbprint = cert_thumbprint; ctx->password = password; } diff --git a/Инструкция по установке.md b/Инструкция по установке.md index 160acf0..be379e9 100644 --- a/Инструкция по установке.md +++ b/Инструкция по установке.md @@ -46,13 +46,13 @@ chown -R ervu:ervu /var/opt/cprocsp/keys/ervu/key_cont.000/ ``` Получить следующие значения: -- Поле "Субъект": любая подстрока из этого поля является signer_subject (нужно будет указать в конфигурационном файле модуля подписания) +- Поле "SHA1 отпечаток": это значение нужно будет указать в конфигурационном файле модуля подписания в качестве настройки \ - Поле "Ссылка на ключ": убедиться, что значение равно "Есть" (иначе необходимо проверить выполнение предыдущих шагов) -4. (опционально) Сделать тестовую подпись с помощью установленного контейнера и полученных значений \ и \: +4. (опционально) Сделать тестовую подпись с помощью установленного контейнера и полученных значений \ и \: ``` bash touch test.txt -/opt/cprocsp/bin/amd64/cryptcp -signf -dn ./test.txt +/opt/cprocsp/bin/amd64/cryptcp -signf -thumbprint ./test.txt rm -f test.txt test.txt.sgn ``` From 3acd286ae02f01bfd9f3baabd40d82ce3b24a748 Mon Sep 17 00:00:00 2001 From: alashkova Date: Tue, 22 Oct 2024 16:40:03 +0300 Subject: [PATCH 25/37] =?UTF-8?q?SUPPORT-8592.=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20location=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BD?= =?UTF-8?q?=D0=BE=D0=BC=D0=B5=D1=80=D0=B0=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/service_version.c | 60 +++++++++++++++++++++++++++++++++++ src/modules/service_version.h | 22 +++++++++++++ src/service_manager.c | 13 ++++++++ src/service_manager.h | 2 ++ 4 files changed, 97 insertions(+) create mode 100644 src/modules/service_version.c create mode 100644 src/modules/service_version.h diff --git a/src/modules/service_version.c b/src/modules/service_version.c new file mode 100644 index 0000000..7dca8ab --- /dev/null +++ b/src/modules/service_version.c @@ -0,0 +1,60 @@ +#include "service_version.h" + +#include "version.h" + +#include "fcgisrv/fcgi_utils.h" + +#include "utils/logger.h" + +#include + + +#define VERSION_TEXT \ + "Content-type: text/plain" CRLF CRLF \ + APP_VERSION + +static const str_t VERSION_CONF_DEFAULT_LOCATION = str_t_const("/version"); + + +int +version_conf_load(version_conf_t *conf) +{ + LOG_TRACE("version_conf_load enter"); + + assert(conf != NULL); + + conf->location = &VERSION_CONF_DEFAULT_LOCATION; + + LOG_TRACE("version_conf_load exit"); + return 0; +} + +void +version_conf_clear(version_conf_t *conf) +{ + LOG_TRACE("version_conf_load enter"); + + if (conf != NULL) { + conf->location = NULL; + } + + LOG_TRACE("version_conf_load exit"); +} + +fcgi_handler_status_t +fcgi_version_handler(FCGX_Request* request, void* ctx) +{ + LOG_TRACE("fcgi_version_handler enter"); + + assert(request != NULL); + + if (request == NULL) { + LOG_ERROR("fcgi_version_handler failed: request is NULL"); + return HANDLER_ERROR; + } + + FCGX_FPrintF(request->out, VERSION_TEXT); + + LOG_TRACE("fcgi_version_handler exit"); + return HANDLER_SUCCESS; +} diff --git a/src/modules/service_version.h b/src/modules/service_version.h new file mode 100644 index 0000000..18e59f0 --- /dev/null +++ b/src/modules/service_version.h @@ -0,0 +1,22 @@ +#ifndef SERVICE_VERSION_H_INCLUDED +#define SERVICE_VERSION_H_INCLUDED + + +#include "fcgisrv/fcgi_server.h" + +#include "utils/str_t.h" + + +typedef struct version_conf_s { + const str_t *location; + +} version_conf_t; + + +int version_conf_load(version_conf_t *conf); +void version_conf_clear(version_conf_t *conf); + +fcgi_handler_status_t fcgi_version_handler(FCGX_Request* request, void* ctx); + + +#endif // SERVICE_VERSION_H_INCLUDED diff --git a/src/service_manager.c b/src/service_manager.c index e791ea3..03092e6 100644 --- a/src/service_manager.c +++ b/src/service_manager.c @@ -166,6 +166,11 @@ service_manager_load_conf(const char* config_file_name, service_manager_conf_t* goto error; } + if (version_conf_load(&services_cf->version_cf)) { + LOG_ERROR("Show version service configuraton loading failed"); + goto error; + } + conf_file_close_file(conf_file); LOG_TRACE("service_manager_load_conf exit with success"); @@ -185,6 +190,7 @@ service_manager_clear_conf(service_manager_conf_t* services_cf) { assert(services_cf != NULL); + version_conf_clear(&services_cf->version_cf); sign_conf_clear(&services_cf->sign_cf); verify_conf_clear(&services_cf->verify_cf); fcgi_clear_conf(&services_cf->fcgi_cf); @@ -256,6 +262,13 @@ init_services(service_manager_t* services, const service_manager_conf_t* service goto error; } + /* show version service */ + if (register_service(services, services_cf, fcgi_version_handler, NULL, + services_cf->version_cf.location)) { + LOG_ERROR("Could not register 'show version service'"); + goto error; + } + /* run fastcgi server */ if (fcgi_run_server(services->hfcgi) != 0) { LOG_ERROR("Could not run FastCGI server"); diff --git a/src/service_manager.h b/src/service_manager.h index 683fdb7..7c30f88 100644 --- a/src/service_manager.h +++ b/src/service_manager.h @@ -9,6 +9,7 @@ #include "modules/service_sign.h" #include "modules/service_verify.h" +#include "modules/service_version.h" struct service_s; @@ -48,6 +49,7 @@ typedef struct service_manager_conf_s { main_conf_t main_cf; sign_conf_t sign_cf; verify_conf_t verify_cf; + version_conf_t version_cf; } service_manager_conf_t; From 52ab51116a383d9d84f8a74a58c4950dfe247f70 Mon Sep 17 00:00:00 2001 From: alashkova Date: Tue, 22 Oct 2024 16:41:04 +0300 Subject: [PATCH 26/37] =?UTF-8?q?SUPPORT-8592.=20location=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=BD=D0=BE=D0=BC=D0=B5=D1=80=D0=B0=20=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D1=81=D0=B8=D0=B8:=20CMakeLists.txt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c01c1dd..d790521 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,6 +131,7 @@ ADD_EXECUTABLE (${PROJECT_NAME} ${FCGISRV_DIR}/fcgi_worker.c ${MODULES_DIR}/service_sign.c ${MODULES_DIR}/service_verify.c + ${MODULES_DIR}/service_version.c ) TARGET_LINK_LIBRARIES (${PROJECT_NAME} ${DEP_LIBS}) From 65b553b61edf73b0a393b023f7af53c49204d2b4 Mon Sep 17 00:00:00 2001 From: alashkova Date: Tue, 22 Oct 2024 16:50:33 +0300 Subject: [PATCH 27/37] =?UTF-8?q?SUPPORT-8592.=20=D0=92=D0=B5=D1=80=D1=81?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F:=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20nginx.conf=20(=D0=B2=D1=81=D0=B5=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D1=8B=20=D0=BF=D1=80=D0=BE=D0=BA=D1=81=D0=B8?= =?UTF-8?q?=D1=80=D1=83=D1=8E=D1=82=D1=81=D1=8F=20=D0=BD=D0=B0=20fastcgi-?= =?UTF-8?q?=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- conf/nginx-docker.conf | 7 +------ conf/nginx.conf | 7 +------ src/modules/service_version.c | 2 +- Инструкция по установке.md | 12 +++++++++++- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 8b6e752..1855d16 100644 --- a/README.md +++ b/README.md @@ -89,10 +89,10 @@ fcgi_listen_host = 127.0.0.1 *\# значение по умолчанию: 127.0 fcgi_thread_pool_size = 1 *\# значение по умолчанию: 1* - В секции **\[sign\]** задать настройки модуля подписания: -location = /sign *\# значение по умолчанию: /sign, должно совпадать со значением в nginx.conf* +location = /sign *\# значение по умолчанию: /sign* sign_cert_thumbprint = sha1_thumbprint_of_signer_cert *\# SHA1 отпечаток сертификата, которым ИС подписывает секрет* sign_cert_password = \*\*\*\* *\# пароль от контейнера* - В секции **\[verify\]** задать настройки проверки подписи маркера доступа: -location = /verify *\# значение по умолчанию: /verify, должно совпадать со значением в nginx.conf* +location = /verify *\# значение по умолчанию: /verify* esia_cert_thumbprint = sha1_thumbprint_of_esia_cert *\# SHA1 отпечаток сертификата ЕСИА* diff --git a/conf/nginx-docker.conf b/conf/nginx-docker.conf index 6c00803..7916831 100644 --- a/conf/nginx-docker.conf +++ b/conf/nginx-docker.conf @@ -2,12 +2,7 @@ server { listen 80; server_name localhost; - location = /sign { - fastcgi_pass ervu-sign-module:9009; - include fastcgi_params; - } - - location = /verify { + location / { fastcgi_pass ervu-sign-module:9009; include fastcgi_params; } diff --git a/conf/nginx.conf b/conf/nginx.conf index eacc93a..3498317 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -35,12 +35,7 @@ http { listen 80; server_name localhost; - location = /sign { - fastcgi_pass localhost:9009; - include fastcgi_params; - } - - location = /verify { + location / { fastcgi_pass localhost:9009; include fastcgi_params; } diff --git a/src/modules/service_version.c b/src/modules/service_version.c index 7dca8ab..99ae100 100644 --- a/src/modules/service_version.c +++ b/src/modules/service_version.c @@ -11,7 +11,7 @@ #define VERSION_TEXT \ "Content-type: text/plain" CRLF CRLF \ - APP_VERSION + APP_VERSION CRLF static const str_t VERSION_CONF_DEFAULT_LOCATION = str_t_const("/version"); diff --git a/Инструкция по установке.md b/Инструкция по установке.md index be379e9..2495335 100644 --- a/Инструкция по установке.md +++ b/Инструкция по установке.md @@ -161,4 +161,14 @@ curl -v http://127.0.0.1/sign -H "Content-Type: text/plain" -d "test" < REFlyzGQrCjX9DvA7hWwN9vf5kPqBxcG4TLYnXUHnAS9_G-sLAFvaJei2OhxpaWNraHbOv_mMsM_bcDsXWiC0Q * Connection #0 to host 127.0.0.1 left intact -``` \ No newline at end of file +``` + +### Версия приложения + +Для того, чтобы узнать версию приложения, введите команду: + +``` bash +curl -v http://127.0.0.1/version +``` + +В ответе будет получена версия приложения (статус-код ответа должен быть равен 200 OK). From 667be0d88c747f65cd0b5104f12b0a642022ea6b Mon Sep 17 00:00:00 2001 From: alashkova Date: Thu, 24 Oct 2024 13:01:19 +0300 Subject: [PATCH 28/37] SUPPORT-8592. fcgi_unregister_handlers() fixed --- src/fcgisrv/fcgi_server.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fcgisrv/fcgi_server.c b/src/fcgisrv/fcgi_server.c index 68e39cb..80a6260 100644 --- a/src/fcgisrv/fcgi_server.c +++ b/src/fcgisrv/fcgi_server.c @@ -233,6 +233,8 @@ fcgi_register_handler(HFcgi hfcgi, char* path, fcgi_handler_t* handler) static void fcgi_unregister_handlers(HFcgi hfcgi) { + if (hfcgi == NULL) return; + fcgi_handler_map_clear(&hfcgi->handler_map); } From 8f52e508d21977879dc6bef2053e8e7afe493d6e Mon Sep 17 00:00:00 2001 From: alashkova Date: Thu, 24 Oct 2024 16:29:16 +0300 Subject: [PATCH 29/37] SUPPORT-8592. version_conf_clear(): logging fixed --- src/modules/service_version.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/service_version.c b/src/modules/service_version.c index 99ae100..b10ce6d 100644 --- a/src/modules/service_version.c +++ b/src/modules/service_version.c @@ -32,13 +32,13 @@ version_conf_load(version_conf_t *conf) void version_conf_clear(version_conf_t *conf) { - LOG_TRACE("version_conf_load enter"); + LOG_TRACE("version_conf_clear enter"); if (conf != NULL) { conf->location = NULL; } - LOG_TRACE("version_conf_load exit"); + LOG_TRACE("version_conf_clear exit"); } fcgi_handler_status_t From fdaf3446c8eaf756c1d2bf138c33095c725c8b50 Mon Sep 17 00:00:00 2001 From: alashkova Date: Thu, 24 Oct 2024 16:30:54 +0300 Subject: [PATCH 30/37] SUPPORT-8592. fcgi_version_handler(): minor fix --- src/modules/service_version.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/service_version.c b/src/modules/service_version.c index b10ce6d..c6313db 100644 --- a/src/modules/service_version.c +++ b/src/modules/service_version.c @@ -46,8 +46,6 @@ fcgi_version_handler(FCGX_Request* request, void* ctx) { LOG_TRACE("fcgi_version_handler enter"); - assert(request != NULL); - if (request == NULL) { LOG_ERROR("fcgi_version_handler failed: request is NULL"); return HANDLER_ERROR; From dff9c9bf2dd09a87f292a9018176ec36ce5bf565 Mon Sep 17 00:00:00 2001 From: alashkova Date: Thu, 24 Oct 2024 16:37:49 +0300 Subject: [PATCH 31/37] =?UTF-8?q?SUPPORT-8592.=20=D0=92=D0=B5=D1=80=D1=81?= =?UTF-8?q?=D0=B8=D1=8F=201.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- ...укция по обновлению с версии 1.0.0 до 1.1.0.txt | 109 ++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 Инструкция по обновлению с версии 1.0.0 до 1.1.0.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index d790521..7b6c24f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ CMAKE_MINIMUM_REQUIRED (VERSION 3.0) SET (CMAKE_C_COMPILER "gcc") -PROJECT (ervu-sign-module VERSION 1.0.0 LANGUAGES C) +PROJECT (ervu-sign-module VERSION 1.1.0 LANGUAGES C) IF (CMAKE_VERBOSE) SET (CMAKE_VERBOSE_MAKEFILE 1) diff --git a/Инструкция по обновлению с версии 1.0.0 до 1.1.0.txt b/Инструкция по обновлению с версии 1.0.0 до 1.1.0.txt new file mode 100644 index 0000000..b65d0bd --- /dev/null +++ b/Инструкция по обновлению с версии 1.0.0 до 1.1.0.txt @@ -0,0 +1,109 @@ +В версии 1.1.0 появились следующие функции: +- Проверка подписи маркера доступа +- Вывод номера версии приложения + +Для обновления приложения необходимо выполнить следующие шаги (команды выполняются от имени root): + +1. Остановить службу: +``` +systemctl stop ervu-sign-module +``` + +2. Обновить приложение: +``` +cp ./ervu-sign-module /opt/ervu-sign-module/ +``` + +3. Обновить конфигурационный файл /etc/ervu-sign-module.conf. Для этого: + +3.1. Перенести настройку cp_file из секции [sign] в секцию [main] + +3.2. Задать SHA1 отпечаток сертификата, которым ИС подписывает секрет, для этого: + +3.2.1. В конфигурационном файле получить значение + +3.2.2. Получить перечень свойств сертификата (команда выполняется от имени ervu): +``` +/opt/cprocsp/bin/amd64/certmgr -list -dn +``` + +Из полученного перечня свойств сертификата необходимо получить поле "SHA1 отпечаток". + +3.2.3. В конфигурационном файле в секции [sign] задать полученное значение полю sign_cert_thumbprint: +[sign] +sign_cert_thumbprint = <значение поля "SHA1 отпечаток", полученное на предыдущем шаге> + +3.2.4. Настройку signer_subject нужно удалить. + +3.3. Переименовать настройку с паролем от контейнера (было - pin, стало - sign_cert_password) +[sign] +sign_cert_password = <пароль, который раньше задавался в поле pin> + +4. Установить сертификат ЕСИА. Для этого: + +4.1. Скачать сертификаты тестовой и продуктивной сред ЕСИА, используемые для формирования электронных подписей ответов как поставщика, по ссылке: https://esia.gosuslugi.ru/public/esia.zip + +4.2. Из списка сертификатов выбрать тот, который подходит под конфигурацию стенда (в зависимости от алгоритма подписи и от того, является ли среда тестовой или продуктивной): + +| Среда \ Алгоритм подписания | RS256 | GOST3410_2012_256 | +|-----------------------------|-----------------------------------------------------|-----------------------------------------------------| +| | RSA_TESIA_2024.cer | TESIA GOST 2012 new.cer | +| Тестовая | Отпечаток: 6a3813240f6cc285e6d4e6d9a4bfeb26038195d8 | Отпечаток: e654f6114dc19bae84c5748794a4fd7797726e71 | +| | Срок действия: по 06.08.2034 | Срок действия: по 24.04.2025 | +|-----------------------------|-----------------------------------------------------|-----------------------------------------------------| +| | RSA_PROD_2024.cer | ГОСТ+ПРОД+24-25.cer | +| Продуктивная | Отпечаток: 29c2f9c8e064d73509eadd685a5508712735e303 | Отпечаток: 20ad21785b9161ef46f075d4dc23c34e1e349228 | +| | Срок действия: по 12.05.2025 | Срок действия: по 08.06.2025 | +------------------------------------------------------------------------------------------------------------------------------------------- + +4.3. Установить выбранный сертификат (команда выполняются от имени ervu): +``` +/opt/cprocsp/bin/amd64/certmgr -install -file '.cer' +``` + +Результатом команды будет список полей сертификата. Из него необходимо получить следующие значения: +- Поле "SHA1 отпечаток": это значение нужно будет указать в конфигурационном файле в качестве настройки . +- Поле "Алгоритм подписи": проверить, что значение совпадает с настройками ИС в ЕСИА. +- Поле "Истекает": проверить, что срок действия сертификата не истёк. + +4.4. В конфигурационном файле добавить секцию [verify]: +[verify] +#location = /verify +esia_cert_thumbprint = + +4.5. Прописать SHA1 отпечаток сертификата ЕСИА: +[verify] +esia_cert_thumbprint = <значение, полученное на шаге 4.3.> + +Примечание. Пример конфигурационного файла можно посмотреть в conf/ervu-sign-module.conf.example + +5. Запустить службу: +``` +systemctl start ervu-sign-module +``` + +6. Обновить конфигурационный файл /etc/nginx/nginx.conf: +заменить строку: + +``` +location = /sign { +``` + +на: + +``` +location / { +``` + +7. Перезапустить nginx: +``` +systemctl reload nginx +``` + +8. Проверить версию приложения: +``` +curl -v http://127.0.0.1/version +``` + +Статус-код ответа должен быть равен 200 OK. +В ответе должна быть возвращена строчка "1.1.0". From 10af38bcd0f3226d329986704023f03a446e1838 Mon Sep 17 00:00:00 2001 From: alashkova Date: Thu, 7 Nov 2024 14:30:41 +0300 Subject: [PATCH 32/37] =?UTF-8?q?SUPPORT-8592.=20=D0=92=20=D0=B8=D0=BD?= =?UTF-8?q?=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=86=D0=B8=D1=8E=20=D0=BF=D0=BE?= =?UTF-8?q?=20=D1=83=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BA=D0=B5=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=B8?= =?UTF-8?q?=D0=BD=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=86=D0=B8=D0=B8=20=D0=BF?= =?UTF-8?q?=D0=BE=20=D1=83=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BA=D0=B5?= =?UTF-8?q?=20=D0=9A=D1=80=D0=B8=D0=BF=D1=82=D0=BE=D0=9F=D1=80=D0=BE=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...укция по обновлению с версии 1.0.0 до 1.1.0.txt | 4 +++- Инструкция по установке.md | 23 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Инструкция по обновлению с версии 1.0.0 до 1.1.0.txt b/Инструкция по обновлению с версии 1.0.0 до 1.1.0.txt index b65d0bd..69f5060 100644 --- a/Инструкция по обновлению с версии 1.0.0 до 1.1.0.txt +++ b/Инструкция по обновлению с версии 1.0.0 до 1.1.0.txt @@ -56,7 +56,7 @@ sign_cert_password = <пароль, который раньше задавалс | | Срок действия: по 12.05.2025 | Срок действия: по 08.06.2025 | ------------------------------------------------------------------------------------------------------------------------------------------- -4.3. Установить выбранный сертификат (команда выполняются от имени ervu): +4.3. Установить выбранный сертификат (команда выполняется от имени ervu): ``` /opt/cprocsp/bin/amd64/certmgr -install -file '.cer' ``` @@ -95,6 +95,8 @@ location = /sign { location / { ``` +Примечание. Пример конфигурационного файла можно посмотреть в conf/nginx.conf + 7. Перезапустить nginx: ``` systemctl reload nginx diff --git a/Инструкция по установке.md b/Инструкция по установке.md index 2495335..4cd6f2a 100644 --- a/Инструкция по установке.md +++ b/Инструкция по установке.md @@ -11,11 +11,21 @@ adduser --system --no-create-home --user-group ervu 1. Скачать КриптоПро 5 R3 КС3. -2. Установить КриптоПро. Для этого выполнить команды от имени root: +2. Установить КриптоПро. +2.1. Для продуктивного контура: +2.1.1. Установить КриптоПро c конфигурацией kc2. Для этого выполнить команды от имени root: ``` bash tar -zxvf linux-amd64.tgz && cd ./linux-amd64/ -./install.sh -apt-get install ./cprocsp-pki-cades-64-2.0.14815-1.amd64.rpm +./install.sh kc2 cprocsp-pki-cades +cd - +``` +2.1.2. Установить ПАК "Соболь" по инструкции: https://support.cryptopro.ru/index.php?/Knowledgebase/Article/View/285/2/ispolzovnie-pak-sobol-v-kchestve-dsch-v-kriptopro-csp-n-linux + +2.2. Для тестового контура: +2.2.1. Установить КриптоПро c конфигурацией kc1. Для этого выполнить команды от имени root: +``` bash +tar -zxvf linux-amd64.tgz && cd ./linux-amd64/ +./install.sh kc1 cprocsp-pki-cades cd - ``` @@ -172,3 +182,10 @@ curl -v http://127.0.0.1/version ``` В ответе будет получена версия приложения (статус-код ответа должен быть равен 200 OK). + + +### Примечания + +Подробнее о работе с КриптоПро CSP здесь: +- https://www.altlinux.org/%D0%9A%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%9F%D1%80%D0%BE +- https://wiki.astralinux.ru/pages/viewpage.action?pageId=32833902&ysclid=m2si404crb648040570 From f8ba47b512f810a5de4245247609bf4c3372fae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A5=D0=B0=D0=BB=D1=82=D0=BE=D0=B1=D0=B8=D0=BD=20=D0=95?= =?UTF-8?q?=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9?= Date: Tue, 12 Nov 2024 10:11:06 +0300 Subject: [PATCH 33/37] fixed build added ESIA cert, CA CRL, CA cert --- Dockerfile.micord | 14 ++++++- conf/TESIA GOST 2012 new.cer | Bin 0 -> 2274 bytes ...fd8eb959d9489d5b7b4c143a06cad7952a0744.crl | Bin 0 -> 11599 bytes conf/test_ca_rtk3.cer | 36 ++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 conf/TESIA GOST 2012 new.cer create mode 100644 conf/b0fd8eb959d9489d5b7b4c143a06cad7952a0744.crl create mode 100644 conf/test_ca_rtk3.cer diff --git a/Dockerfile.micord b/Dockerfile.micord index de8d1a0..50e5982 100644 --- a/Dockerfile.micord +++ b/Dockerfile.micord @@ -13,7 +13,8 @@ RUN apt-get update \ cmake \ make \ gcc \ - gcc10 + gcc10 \ + libjson-glib libjson-glib-devel WORKDIR /build COPY src src @@ -52,4 +53,15 @@ EXPOSE 9009 USER ervu +ARG ESIA_CERT="TESIA GOST 2012 new.cer" +ARG ESIA_CA_CRL=b0fd8eb959d9489d5b7b4c143a06cad7952a0744.crl +ARG ESIA_CA_CERT=test_ca_rtk3.cer +COPY --chown=ervu:ervu conf/${ESIA_CERT} ${ESIA_CERT} +COPY --chown=ervu:ervu conf/${ESIA_CA_CRL} ${ESIA_CA_CRL} +COPY --chown=ervu:ervu conf/${ESIA_CA_CERT} ${ESIA_CA_CERT} + +RUN ls -la && /opt/cprocsp/bin/amd64/certmgr -install -file "${ESIA_CERT}" \ + && /opt/cprocsp/bin/amd64/certmgr -install -store ca -crl -file "${ESIA_CA_CRL}" \ + && /opt/cprocsp/bin/amd64/certmgr -install -store ca -file "${ESIA_CA_CERT}" + ENTRYPOINT ["/entrypoint.sh"] diff --git a/conf/TESIA GOST 2012 new.cer b/conf/TESIA GOST 2012 new.cer new file mode 100644 index 0000000000000000000000000000000000000000..69a3d5c9593fe806376f018614feea6dc57ac04f GIT binary patch literal 2274 zcmeHJT}%{L6lQ*0799Koff~^pgjR7G&dkm-yG9!Y+u%>q`qx-%R9MiqV#Nhts);j0 zAsT9-QjJ|jK+&jA3%EKg>?&#cV$$?no0hbiHce`Lv`L#ZP1D%i8CJzKP17{#OOr|F zo^#L4Irsa%b7sKuehZejqfg*Do}bOH$-bMheem9y^@n;to49yfg=~+vtWC(|IDrR? zGnKiJ>zP*8Ce({_vUMGF&_e!TP$`6b*UinzQ9#vo03{UED|KZ$WO;-wq9>Q%xJ6k4 zFEGO^5j{w-A7hxr1hvYs7vt2zI5lvPwo(I!mFJ;^g=R`ce;sF@1M>`v%#zG*9HXt6 zM4PswjW;odqx5}D(heLUn8E}#Xe$o0#ABFp_t3^9H8FuHj1xS8Ju;rhlh`L?x9nq) zHYPEJH=URm?If7Q7!Eq6#_>9{Xs3_hHB8|cQ8XXHQM4z|5+m4^K1jAyguU2>=SVU3 zVTzh821haODSpuXu&p3R z4Y;PlOt84i&yb{mrfYsZpb%K#$tr8FY5ygM%i^WkIDp-F8v6;J#C|-DUC+>{={l&O zR_dTA&p`pJE?b&~XC2Ji(WdWFgLWwEV2z7^74}ktm5xb_Pr~{HG6v^0SpgqmXQPI! z`pB9M<(R-Bj8TJHj4r2ihjt0iGL+nHWj@SfZm*Q9>jZ~oGG%v)#>~rLUSz?7WcIkl zqV2*>4d*_DDUzspW-bF`cmszV`fSE3HF1b`;tjM3yT&j^O=`Nd&^F>Sm)0C6 zFy`oPgi*x^HqpkBNpX!39KbkUbA;o@*{~ZHrrV3-AlH*w)+X?LNk-bq^Fnooxbn;K z1EtYZ$Co!9dl0x))%qj4o6~!^ z^+?gpM;i)#zAKeS-o9XoSHTi5!3>YErD!I{JrYHM7k`8zX3r4w#k}Q@Cgyd2`qR&1 z^y=;VRowI%qi3PJT`J~7p2xe?!%dr(De#Y1B5Y2tIZmvBmtl3<2EIM`W=l)cvWkju zL!_nQoyh*iM!BV-xkcWyzxiNu|$&rH<;rgZu=G_^t-5F`w>zBg~5jQDeNx|#! zut{@(d%WUdDJyxFSOUeyOckbdg2^0{z2LM4Se))or6$qhd|+{lCAAo%)L_$rT9|SK zn=G-nRkig`?F_QPfm$Oo2V?h7WK_lIy>T_H7F~< zQmC-P1#Z8n{S(Nbfzh;3+yUERTi@2c*V;G#T|v~r?)qRuT__N$4{5K%!~m*&Q< RpYnZN<$1XF>cfZk{{YwQ^LqdQ literal 0 HcmV?d00001 diff --git a/conf/b0fd8eb959d9489d5b7b4c143a06cad7952a0744.crl b/conf/b0fd8eb959d9489d5b7b4c143a06cad7952a0744.crl new file mode 100644 index 0000000000000000000000000000000000000000..51ebdaa21b4ddeab1a6ee2d3b67b368dd9142cf4 GIT binary patch literal 11599 zcmai)cUToi*T9FO0`{)4p|PUTnJssBt#r6>0gVNtv1{H$6MH2VKokT8RBR|x#jZOb z3P>^b_Qu5+uwsiPMx(}FqTe$!H+jjjTQ8UX~|knNw}6%5~|^n#U-IyHtGo5 zlSgaRYW45Wg>8mm8_AQ7wZc|w1tp8MLM^vssg|o1YpQmzWVu#Y5~JlgYeiaqNodJp zEt?1~(2Cf1TCP@D5~k&AMOu!twn|ItuBB;fw3P1J>h9ghlet=a4~8Wr$O)R$8(auWfd2r=@5`C1KcG2R0zm(kb?+Hyuc~LU@hoW-EMHJ#`bnjHq|Jx zN?rr<)feGzOYZLscyJ_x5)u>;&&dcG5s!NkqCrMbK>57#OvF(_K|B&fftQVlaeW9; zvdZt&X!md3&TMDeLk>z%5=A2-@#7AVuhuE{ZX+ai=-=qurnNa9anJ;1ByoZf;n4XR zIH+dsa$WLq*pOhyl~zJPBIHHRh}hjW1Qe)8vV%f9we}1P7)S|(Oo$T|-tb z`zWJn_Kv>^PUMl!gG{TKvr(WxoxjWHi#dA+^k`RNB_za)vVL_LkvM51C{W{zJvM|d z9{Q)(xr&6bjKVbupim9j;MVHh^rxXs3aHOzD3L8G>g-_qYQXDIa9G{G%u6~mupnnj z9wj(vLZT=MIy)EXjL+chvfsc7waw;$aVTBe zG=bTH6vT5P(rLx)ICX;%E%yaI4{Hd%b0sHAfHL_LEA0|S%)3R1X9t5i`L@4R|9uTg zNKio>2leNJ5q0kp;!L3>W%%j`b(1uv6(=JBa-89*oj4>7oK%M<`Uw9yHZ(IO&T5AU z6@fR73}twLCN)oPvlj@uGenV#T+q)p+2D2P- zP=UOjV0L`deHl2VI&b!=f4iyuJzs?qCdebm%lcTti060Kf>Uapt$}dbYlkV#Nu=fx z;uNSLhzw&%{-fvMN7W`f;K;0_&2QYgLkSMbbbWcFo%r%>Bq&ikEp=Pqyu3^OQP0_0 zky9v&ykQhgbWR69s~-w2{t3^owaq=lWRO$N@z9W%C+`BK>iBhj`z(>qT|3mV$}mBR z`Dm< zaY%qXG}LN4e;<^o{k8^78<;b1^8E&k-~=RyqVZZKJ$(Sm)Y=DJ9rtg}R8po=LPiRd zI1|#HL1uYBN(^7=njKv0%%>eplu(F9TooSN`1vhxN#%4v>xoiGS|gQP#fpqYpL!Pw(JtO?ZkO&$^CsB+P4I{lbh(xh z4p}onx%#-+$0;@H=OPTX~@jWOa_Obf)ss@V*cp( zcp<9MBrq}sFsg(f6HiATmcYS^|Ea9tf4> z%iy|tBFx{%uHnnw6Ax26NJis1MPJDyXB>B%_x=c;tLHNPcULP)E&hty0ZCmVa*Cjf)J!W& z&yC=PTAJ=3u><+P?beNIWug&|=MAIJj9!Fjsk#nKp1khc)WMYCI0;Hf6b*@JO59sv z3E0-x{*h2l37%7s!1KnL(@Y7kE#9s3-HPW`zd{I-YoH`>oS_|2A5MWcYNwc>X-!W% zdADs&Wsn*`L89?)Kiy#_c&kp2bbm9s+C!f~_b5RkHR&iSo%43K^=;oBPXn9PflGbc zUAo@-OyLpg52BSK3Zx<zwpDmWhK6}4a=@U>je;9U-n>Nfv|>46}Wc}_6Q@~0`0 z8xzpi<3`sz`|T?bRqW^z(;)ybO7?yZuZ+*@zlsouxD0V}<&J^anzLaku)(KuJo87a z8M--RN(DOE&&Qkvt;&yH<${B7p_agKMsIJL5IIN5=uDrK?;;b1sc|*ODS;-`-S#w=g_u1oe~lx^*b{5$DX4hpet5$1O2;hSe$&mgRXedUau~D2Bi4K4fqFPSd*^KISsnu#T(47Ja|l79()57I zsM$UgAUu7&xzE6dAF4Sf6B!7Su1ye)J?Jyy3gB^#Q2*EcA|prDeZ#auBAF^0XW%ay zl>;6pX9XVZe{RS7N9(PG3?)I<*I7)f=phk6z%!Pb>kZoD@!{wDf5|`)iu(SR5d%iY z0s-%fFuyGCoRKh`&ap_920?nFXVfTbBWn)mobaV5IIuC3dZ}2YY_uv zd}?=qeG3c5>pNk7M8#LV{>fP$A5c({%%`ffQt&ME_vSfa1^nO3ePdq7Y8F~%}e z$+>&pr-%|VBqvG~4eO(i5oMq!-k0M)`$^wF{Wq~%kZ2{6+2TJl4wG?VVl3!|S7iA8 zuzbe0*lDaI=1D`vRWb7mzmfua;of$T{E}g@Z(g507cRQ=x0o9C&mXH24=h}!Yju= zZ_9C8Hm_y`($|TMSb9kVX8e1+>*?FUk3R|OO=Qr>Kq9Z<`A9q;2?DTNxYzS)GbW9G zOl}+mD3bA|sPMW@lF4`x6AJ=y#8wZFt25iy$m&E16C_cT*LPQpc-npi7>Jvz0TX>= zW9x6G-4zs&iPV11 zSiQ>`-(+R=ivfdi>E^(`2lt%hMzShHB;5oQMZ?{`mJ-cZ`edxiJ-KepWGX{}A~bP| z@zi!IB`)srDzFSKtQGPLArRy^C=o&bj6Q6aJf;L*5|mi{tZtWAEGE)@#q)Z9FrxmD zG%y&C$o0!;Ho>vH4m-8hxruR=(f4Z%g7Mt~znbsf6@is3s|h3?3YG3bm*+;+la_7yg@6*vJ(oT%T4Osk)cjt3*~heFrVaYt>&^B3aeKNpQTz{LT<%nNUQP|81gsji!jKJqJz#Lqb@0k29Ngo4N}LB@9!TY6psbMVA0&&=)B%DUF* z_x}@7+V5a44q4(c@o;a4lXpKdJ2)OnoWSd9QF}JcksgEA*l9(e>$Uphx5?}tgyT3Q z@N~{g`usYdlV@%n2&fbNsOD?$QS?{YGdzjrq}x5Q{xO(~6O+sV-dU&HB(|v_S*jfO z$ghq${sgqf4e~9o7OsDDuxxjQUzLz`dXO^aDVU2B3d|1=FJI{9&MIY+(*<79-yJb~ zdYb(_qRmyqH^+#N1@M(%=`D-*pB$0WI-agsl=X$4d=l; zJavit-V0Lnq~@#-VBX^N(Tw@GNy&V$5J%^ldu=^4WY*TdCs^dJ*|_fX+_n-d#8QH1 z8_y+6`G&DX4;n2+Q84b_ea94oh1etB?Zv$K%jv_;Q_U4R$5#Zy$^WmWqhJyKE!V4K zX<#s@_pi|{xD`T%l6jO zW87H>v?DSy*9Kf&>peCk@UIg}ZUw8TJDVt{K?44w$YZ|w;CDx+<}*7eA?YhTrd4B^ z5ILD{rU@ehUFHp;#J_4aujo2R!2U^Y15E>e@jvKG336);c~nIPTX7Yv#n-cZ4mh4X zD}}zGgf;SWx=xPm#OPQ;lw|wX=`U;_I&~5u$lWyJNkTTtNB!do(PC?Wx82)T>swmy z>v)p6tLV#8*An7Qs&~^C!5jF2HdF?r6LZ-(A8pt`h~Y_IqnBqFx0=j$qSX$)&zT)h zC*A>TvEvGFuhuI&oVmaz9oG9HU+ z#MuWdBO z+wLoLo!<&sHH=%-H+{xJmT?Jcl`xQaUJN@D+ ztf8xmAeqsN$k^v;es{q(d@IYmw)2nwY5aoqGYTSaBvd-J%#P-hW55o4b8n#Qf`RMr zdeJE~nf@Yyll1*Q^G9jKLx6Ews`t#VeNJ9)z8G1_OIXxJ|6A)Zq$#lXZNq8TG6N}^7>gUvtv%f zMDQHF)Sv~GF+3icaZv;t)FIMs9P}>&AK*OwrO=djf z%z;Z+$5>^E&?HnbKgnoCh!aV!PaQ}4_AjGvUyz9oG*z~P`C}y|!j_spi%OHuU7c1z zhG?9X20IfO1LFMVv}k#!>$_?d2*LOsH}An=&^BvUn&*we!8+gyg@l8nzk+Id>yv7~#u4Y;PlOf3Z=wY8S7LJB( z$h)hb+BbtuLK~@n#^!x-$v0{E_m)OItDi1g<5I&D)+7%!vbVRjwVwuCR(I@L-NxP~ zTxx7@Uzhzl;*kkLQgJu<-?ujQ)okr-fo;W?p6%SgBi6(zenEeme2I(N^@ zv0ufOeGJJ-*^`$4;X>O}aUFg4r6tC1ne$b8!oe=LKC6Fa%fdB{+$T89dHTw) Date: Wed, 13 Nov 2024 13:08:58 +0300 Subject: [PATCH 34/37] added libjson-glib --- Dockerfile.micord | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.micord b/Dockerfile.micord index 50e5982..2da20bc 100644 --- a/Dockerfile.micord +++ b/Dockerfile.micord @@ -33,7 +33,7 @@ ENV TZ=Europe/Moscow COPY entrypoint.sh /entrypoint.sh RUN apt-get update \ - && apt-get -y install glib2 libfcgi \ + && apt-get -y install glib2 libfcgi libjson-glib \ && apt-get clean \ && rm -f /var/cache/apt/*.bin \ && rm -f /var/lib/apt/lists/update* \ From e0e7a8b15b7cdacf3160a0eca131975f9482b806 Mon Sep 17 00:00:00 2001 From: alashkova Date: Wed, 13 Nov 2024 13:53:17 +0300 Subject: [PATCH 35/37] =?UTF-8?q?SUPPORT-8592.=20=D0=92=20=D0=B8=D0=BD?= =?UTF-8?q?=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=86=D0=B8=D0=B8=20=D0=B2=20?= =?UTF-8?q?=D1=81=D0=BF=D0=B8=D1=81=D0=BE=D0=BA=20=D0=B7=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=81=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D0=B5=D0=B9=20=D0=BC=D0=BE?= =?UTF-8?q?=D0=B4=D1=83=D0=BB=D1=8F=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=B1=D0=B8=D0=B1=D0=BB=D0=B8=D0=BE=D1=82?= =?UTF-8?q?=D0=B5=D0=BA=D0=B0=20libjson-glib?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Инструкция по сборке.md | 2 +- Инструкция по установке.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Инструкция по сборке.md b/Инструкция по сборке.md index aef7319..5b320af 100644 --- a/Инструкция по сборке.md +++ b/Инструкция по сборке.md @@ -18,7 +18,7 @@ cd - 1. Установить зависимости для сборки приложения: ``` bash apt-get install cmake gcc gcc10 -apt-get install glib2-devel libfcgi-devel +apt-get install glib2-devel libfcgi-devel libjson-glib libjson-glib-devel ``` 2. Собрать приложение из исходников: diff --git a/Инструкция по установке.md b/Инструкция по установке.md index 4cd6f2a..2f1391d 100644 --- a/Инструкция по установке.md +++ b/Инструкция по установке.md @@ -115,7 +115,7 @@ systemctl start nginx 1. Установить зависимости для запуска приложения: ``` bash -apt-get install glib2 libfcgi +apt-get install glib2 libfcgi libjson-glib ``` 2. Скопировать исполняемый файл: From 48ca15427795d58e5a1f597b0e02230d240f50d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A5=D0=B0=D0=BB=D1=82=D0=BE=D0=B1=D0=B8=D0=BD=20=D0=95?= =?UTF-8?q?=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9?= Date: Wed, 13 Nov 2024 16:30:41 +0300 Subject: [PATCH 36/37] fix --- Dockerfile.micord | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.micord b/Dockerfile.micord index 2da20bc..eba38b0 100644 --- a/Dockerfile.micord +++ b/Dockerfile.micord @@ -62,6 +62,6 @@ COPY --chown=ervu:ervu conf/${ESIA_CA_CERT} ${ESIA_CA_CERT} RUN ls -la && /opt/cprocsp/bin/amd64/certmgr -install -file "${ESIA_CERT}" \ && /opt/cprocsp/bin/amd64/certmgr -install -store ca -crl -file "${ESIA_CA_CRL}" \ - && /opt/cprocsp/bin/amd64/certmgr -install -store ca -file "${ESIA_CA_CERT}" + && /opt/cprocsp/bin/amd64/certmgr -install -store root -file "${ESIA_CA_CERT}" ENTRYPOINT ["/entrypoint.sh"] From 8f78945147d1fe5b075894fc5e48e10ab3041cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A5=D0=B0=D0=BB=D1=82=D0=BE=D0=B1=D0=B8=D0=BD=20=D0=95?= =?UTF-8?q?=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9?= Date: Thu, 14 Nov 2024 09:02:06 +0300 Subject: [PATCH 37/37] fix --- Dockerfile.micord | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Dockerfile.micord b/Dockerfile.micord index eba38b0..ac1de79 100644 --- a/Dockerfile.micord +++ b/Dockerfile.micord @@ -51,17 +51,18 @@ COPY --from=builder /build/.build/ervu-sign-module /opt/ervu-sign-module/ervu-si EXPOSE 9009 +ARG ESIA_CA_CERT=test_ca_rtk3.cer +COPY conf/${ESIA_CA_CERT} ${ESIA_CA_CERT} +RUN /opt/cprocsp/bin/amd64/certmgr -install -store mRoot -file "${ESIA_CA_CERT}" + USER ervu ARG ESIA_CERT="TESIA GOST 2012 new.cer" ARG ESIA_CA_CRL=b0fd8eb959d9489d5b7b4c143a06cad7952a0744.crl -ARG ESIA_CA_CERT=test_ca_rtk3.cer COPY --chown=ervu:ervu conf/${ESIA_CERT} ${ESIA_CERT} COPY --chown=ervu:ervu conf/${ESIA_CA_CRL} ${ESIA_CA_CRL} -COPY --chown=ervu:ervu conf/${ESIA_CA_CERT} ${ESIA_CA_CERT} -RUN ls -la && /opt/cprocsp/bin/amd64/certmgr -install -file "${ESIA_CERT}" \ - && /opt/cprocsp/bin/amd64/certmgr -install -store ca -crl -file "${ESIA_CA_CRL}" \ - && /opt/cprocsp/bin/amd64/certmgr -install -store root -file "${ESIA_CA_CERT}" +RUN /opt/cprocsp/bin/amd64/certmgr -install -file "${ESIA_CERT}" \ + && /opt/cprocsp/bin/amd64/certmgr -install -store uCA -crl -file "${ESIA_CA_CRL}" ENTRYPOINT ["/entrypoint.sh"]