diff --git a/src/modules/service_sign.c b/src/modules/service_sign.c index 78bf9f7..828c4bb 100644 --- a/src/modules/service_sign.c +++ b/src/modules/service_sign.c @@ -3,6 +3,8 @@ #include "fcgisrv/fcgi_utils.h" #include "utils/cryptopro.h" +#include "utils/json_writer.h" +#include "utils/uuid.h" #define SIGN_CONF_SECTION "sign" #define SIGN_CONF_KEY_LOCATION "location" @@ -10,9 +12,9 @@ #define SIGN_CONF_KEY_SIGN_CERT_PASSWORD "sign_cert_password" #define FCGI_OK_RESPONSE_FORMAT \ - "Content-type: text/plain" CRLF\ + "Content-type: application/json" CRLF\ CRLF\ - "%.*s" CRLF + "%s" CRLF static const str_t SIGN_CONF_DEFAULT_LOCATION = str_t_const("/sign"); @@ -30,7 +32,7 @@ typedef struct fcgi_sign_request_s { char *content; int content_length; - str_t signed_content; + char *response; } fcgi_sign_request_t; @@ -38,7 +40,7 @@ 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 int sign_content(const sign_service_t *hsign, fcgi_sign_request_t *req_info); +static int sign_client_secret(const sign_service_t *hsign, fcgi_sign_request_t *req_info); int sign_conf_load(sign_conf_t *conf, const conf_file_context_t conf_file) @@ -182,7 +184,7 @@ fcgi_sign_handler(FCGX_Request* request, void* ctx) goto exit; } - if (sign_content(hsign, &req_info)) { + if (sign_client_secret(hsign, &req_info)) { status = HANDLER_ERROR; goto exit; } @@ -204,7 +206,9 @@ fcgi_sign_request_clear(fcgi_sign_request_t *req_info) LOG_TRACE("fcgi_sign_request_clear"); free(req_info->content); - str_t_clear(&(req_info->signed_content)); + free(req_info->response); + + memset(req_info, 0, sizeof(fcgi_sign_request_t)); } static fcgi_handler_status_t @@ -214,11 +218,9 @@ fcgi_ok_handler(const FCGX_Request* request, void *ctx) const fcgi_sign_request_t *req_info = (fcgi_sign_request_t*) ctx; - LOG_DEBUG("response status: " FCGI_OK_RESPONSE_FORMAT, - (int) req_info->signed_content.len, req_info->signed_content.data); - - if (FCGX_FPrintF(request->out, FCGI_OK_RESPONSE_FORMAT, - (int) req_info->signed_content.len, req_info->signed_content.data) < 0) { + LOG_DEBUG("response status: " FCGI_OK_RESPONSE_FORMAT, req_info->response); + + if (FCGX_FPrintF(request->out, FCGI_OK_RESPONSE_FORMAT, req_info->response) < 0) { LOG_ERROR("FCGX_FPrintF() failed"); return HANDLER_ERROR; } @@ -233,10 +235,13 @@ fcgi_request_finalize_handler(fcgi_handler_status_t status) switch (status) { case HANDLER_SUCCESS: - case HANDLER_HTTP_OK: handler = fcgi_ok_handler; break; + case HANDLER_HTTP_OK: + handler = fcgi_200_ok_handler; + break; + case HANDLER_HTTP_BAD_REQUEST: handler = fcgi_400_bad_request_handler; break; @@ -259,23 +264,128 @@ fcgi_request_finalize_handler(fcgi_handler_status_t status) } static int -sign_content(const sign_service_t *hsign, fcgi_sign_request_t *req_info) +generate_client_secret(const fcgi_sign_request_t *req_info, const char *state, + /*out*/ str_t *secret) { - LOG_TRACE("sign_content enter"); + LOG_TRACE("generate_client_secret enter"); - str_t content = { - .data = req_info->content, - .len = req_info->content_length - }; + size_t secret_size = req_info->content_length + strlen(state); - if (cryptopro_sign(&hsign->cryptopro_ctx, &content, &req_info->signed_content)) { + secret->data = malloc(secret_size); + if (secret->data == NULL) { + LOG_ERROR("Could not allocate memory for client_secret (%zd bytes)", secret_size); goto error; } - LOG_TRACE("sign_content exit"); + int len = snprintf(secret->data, secret_size, req_info->content, state); + if (len < 0 || (size_t)len >= secret_size) { + LOG_ERROR("Could not concatenate client_secret"); + goto error; + } + + secret->len = len; + + LOG_TRACE("generate_client_secret exit"); return 0; error: - LOG_ERROR("sign_content exit with error"); + str_t_clear(secret); + LOG_ERROR("generate_client_secret exit with error"); + return -1; +} + +static char * +generate_response(const char *signature, const char *state) +{ + JsonBuilder *jbuilder; + char *response; + + LOG_TRACE("generate_response enter"); + + jbuilder = json_builder_new(); + if (jbuilder == NULL) { + LOG_ERROR("json_builder_new failed"); + goto error; + } + + if (json_builder_begin_object(jbuilder) == NULL) { + LOG_ERROR("json_builder_begin_object failed"); + goto error; + } + + if (json_write_member_string(jbuilder, "signature", signature)) { + goto error; + } + + if (json_write_member_string(jbuilder, "state", state)) { + goto error; + } + + if (json_builder_end_object(jbuilder) == NULL) { + LOG_ERROR("json_builder_end_object failed"); + goto error; + } + + response = json_write_to_str(jbuilder); + + g_object_unref(jbuilder); + + LOG_TRACE("generate_response exit"); + return response; + +error: + if (jbuilder != NULL) { + g_object_unref(jbuilder); + } + LOG_ERROR("generate_response exit with error"); + return NULL; +} + +static int +sign_client_secret(const sign_service_t *hsign, fcgi_sign_request_t *req_info) +{ + str_t secret = str_t_null; + char *state = NULL; + str_t signature = str_t_null; + + LOG_TRACE("sign_client_secret enter"); + + state = generate_uuid4(); + if (state == NULL) { + goto error; + } + + if (generate_client_secret(req_info, state, &secret)) { + goto error; + } + + if (cryptopro_sign(&hsign->cryptopro_ctx, &secret, &signature)) { + goto error; + } + + assert(str_t_is_null_terminated(signature)); + + req_info->response = generate_response(signature.data, state); + + if (req_info->response == NULL) { + goto error; + } + + LOG_DEBUG("state: '%s'", state); + LOG_DEBUG("client secret: '%.*s'", (int) secret.len, secret.data); + LOG_DEBUG("response: '%s'", req_info->response); + + str_t_clear(&secret); + free(state); + str_t_clear(&signature); + + LOG_TRACE("sign_client_secret exit"); + return 0; + +error: + str_t_clear(&secret); + free(state); + str_t_clear(&signature); + LOG_ERROR("sign_client_secret exit with error"); return -1; } \ No newline at end of file