mirror of
https://github.com/lammertb/libhttp.git
synced 2025-12-22 04:02:04 +03:00
Client API using SSL certificates (draft, Step 1/?)
This commit is contained in:
@@ -259,7 +259,6 @@ add_c_compiler_flag(-Wno-format-nonliteral)
|
|||||||
if (MINGW)
|
if (MINGW)
|
||||||
add_c_compiler_flag(-Wno-format)
|
add_c_compiler_flag(-Wno-format)
|
||||||
endif()
|
endif()
|
||||||
if
|
|
||||||
if (NOT CIVETWEB_ALLOW_WARNINGS)
|
if (NOT CIVETWEB_ALLOW_WARNINGS)
|
||||||
add_c_compiler_flag(-Werror)
|
add_c_compiler_flag(-Werror)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -751,6 +751,21 @@ CIVETWEB_API struct mg_connection *mg_connect_client(const char *host,
|
|||||||
char *error_buffer,
|
char *error_buffer,
|
||||||
size_t error_buffer_size);
|
size_t error_buffer_size);
|
||||||
|
|
||||||
|
|
||||||
|
struct mg_client_options {
|
||||||
|
const char *host;
|
||||||
|
int port;
|
||||||
|
const char *client_cert;
|
||||||
|
const char *server_cert;
|
||||||
|
/* TODO: add more data */
|
||||||
|
};
|
||||||
|
|
||||||
|
CIVETWEB_API struct mg_connection *
|
||||||
|
mg_connect_client_secure(const struct mg_client_options *client_options,
|
||||||
|
char *error_buffer,
|
||||||
|
size_t error_buffer_size);
|
||||||
|
|
||||||
|
|
||||||
enum { TIMEOUT_INFINITE = -1 };
|
enum { TIMEOUT_INFINITE = -1 };
|
||||||
|
|
||||||
/* Wait for a response from the server
|
/* Wait for a response from the server
|
||||||
|
|||||||
177
src/civetweb.c
177
src/civetweb.c
@@ -9932,6 +9932,8 @@ sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *))
|
|||||||
if (ret != 1) {
|
if (ret != 1) {
|
||||||
err = SSL_get_error(conn->ssl, ret);
|
err = SSL_get_error(conn->ssl, ret);
|
||||||
(void)err; /* TODO: set some error message */
|
(void)err; /* TODO: set some error message */
|
||||||
|
SSL_free(conn->ssl);
|
||||||
|
conn->ssl = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9939,6 +9941,8 @@ sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *))
|
|||||||
if (ret != 1) {
|
if (ret != 1) {
|
||||||
err = SSL_get_error(conn->ssl, ret);
|
err = SSL_get_error(conn->ssl, ret);
|
||||||
(void)err; /* TODO: set some error message */
|
(void)err; /* TODO: set some error message */
|
||||||
|
SSL_free(conn->ssl);
|
||||||
|
conn->ssl = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10095,6 +10099,50 @@ verify_ssl_client(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
ssl_use_pem_file(struct mg_context *ctx, const char *pem)
|
||||||
|
{
|
||||||
|
if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0) {
|
||||||
|
mg_cry(fc(ctx),
|
||||||
|
"%s: cannot open certificate file %s: %s",
|
||||||
|
__func__,
|
||||||
|
pem,
|
||||||
|
ssl_error());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* could use SSL_CTX_set_default_passwd_cb_userdata */
|
||||||
|
|
||||||
|
if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0) {
|
||||||
|
mg_cry(fc(ctx),
|
||||||
|
"%s: cannot open private key file %s: %s",
|
||||||
|
__func__,
|
||||||
|
pem,
|
||||||
|
ssl_error());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSL_CTX_check_private_key(ctx->ssl_ctx) == 0) {
|
||||||
|
mg_cry(fc(ctx),
|
||||||
|
"%s: certificate and private key do not match: %s",
|
||||||
|
__func__,
|
||||||
|
pem);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem) == 0) {
|
||||||
|
mg_cry(fc(ctx),
|
||||||
|
"%s: cannot use certificate chain file %s: %s",
|
||||||
|
__func__,
|
||||||
|
pem,
|
||||||
|
ssl_error());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Dynamically load SSL library. Set up ctx->ssl_ctx pointer. */
|
/* Dynamically load SSL library. Set up ctx->ssl_ctx pointer. */
|
||||||
static int
|
static int
|
||||||
set_ssl_option(struct mg_context *ctx)
|
set_ssl_option(struct mg_context *ctx)
|
||||||
@@ -10160,40 +10208,7 @@ set_ssl_option(struct mg_context *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pem != NULL) {
|
if (pem != NULL) {
|
||||||
if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0) {
|
if (!ssl_use_pem_file(ctx, pem)) {
|
||||||
mg_cry(fc(ctx),
|
|
||||||
"%s: cannot open certificate file %s: %s",
|
|
||||||
__func__,
|
|
||||||
pem,
|
|
||||||
ssl_error());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* could use SSL_CTX_set_default_passwd_cb_userdata */
|
|
||||||
|
|
||||||
if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0) {
|
|
||||||
mg_cry(fc(ctx),
|
|
||||||
"%s: cannot open private key file %s: %s",
|
|
||||||
__func__,
|
|
||||||
pem,
|
|
||||||
ssl_error());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SSL_CTX_check_private_key(ctx->ssl_ctx) == 0) {
|
|
||||||
mg_cry(fc(ctx),
|
|
||||||
"%s: certificate and private key do not match: %s",
|
|
||||||
__func__,
|
|
||||||
pem);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem) == 0) {
|
|
||||||
mg_cry(fc(ctx),
|
|
||||||
"%s: cannot use certificate chain file %s: %s",
|
|
||||||
__func__,
|
|
||||||
pem,
|
|
||||||
ssl_error());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10473,20 +10488,25 @@ mg_close_connection(struct mg_connection *conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct mg_connection *
|
static struct mg_connection *
|
||||||
mg_connect_client(const char *host,
|
mg_connect_client_impl(const struct mg_client_options *client_options,
|
||||||
int port,
|
int use_ssl,
|
||||||
int use_ssl,
|
char *ebuf,
|
||||||
char *ebuf,
|
size_t ebuf_len)
|
||||||
size_t ebuf_len)
|
|
||||||
{
|
{
|
||||||
static struct mg_context fake_ctx;
|
static struct mg_context fake_ctx;
|
||||||
struct mg_connection *conn = NULL;
|
struct mg_connection *conn = NULL;
|
||||||
SOCKET sock;
|
SOCKET sock;
|
||||||
union usa sa;
|
union usa sa;
|
||||||
|
|
||||||
if (!connect_socket(
|
if (!connect_socket(&fake_ctx,
|
||||||
&fake_ctx, host, port, use_ssl, ebuf, ebuf_len, &sock, &sa)) {
|
client_options->host,
|
||||||
|
client_options->port,
|
||||||
|
use_ssl,
|
||||||
|
ebuf,
|
||||||
|
ebuf_len,
|
||||||
|
&sock,
|
||||||
|
&sa)) {
|
||||||
;
|
;
|
||||||
} else if ((conn = (struct mg_connection *)
|
} else if ((conn = (struct mg_connection *)
|
||||||
mg_calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) {
|
mg_calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) {
|
||||||
@@ -10541,16 +10561,53 @@ mg_connect_client(const char *host,
|
|||||||
|
|
||||||
conn->client.is_ssl = use_ssl ? 1 : 0;
|
conn->client.is_ssl = use_ssl ? 1 : 0;
|
||||||
(void)pthread_mutex_init(&conn->mutex, &pthread_mutex_attr);
|
(void)pthread_mutex_init(&conn->mutex, &pthread_mutex_attr);
|
||||||
|
|
||||||
#ifndef NO_SSL
|
#ifndef NO_SSL
|
||||||
if (use_ssl) {
|
if (use_ssl) {
|
||||||
|
fake_ctx.ssl_ctx = conn->client_ssl_ctx;
|
||||||
|
|
||||||
/* TODO: Check ssl_verify_peer and ssl_ca_path here.
|
/* TODO: Check ssl_verify_peer and ssl_ca_path here.
|
||||||
SSL_CTX_set_verify call is needed to switch off server
|
SSL_CTX_set_verify call is needed to switch off server
|
||||||
* certificate checking, which is off by default in OpenSSL and on
|
* certificate checking, which is off by default in OpenSSL and on
|
||||||
* in yaSSL. */
|
* in yaSSL. */
|
||||||
SSL_CTX_set_verify(conn->client_ssl_ctx, SSL_VERIFY_NONE, NULL);
|
|
||||||
// TODO: SSL_CTX_set_verify(conn->client_ssl_ctx, SSL_VERIFY_PEER,
|
// TODO: SSL_CTX_set_verify(conn->client_ssl_ctx, SSL_VERIFY_PEER,
|
||||||
// verify_ssl_server);
|
// verify_ssl_server);
|
||||||
sslize(conn, conn->client_ssl_ctx, SSL_connect);
|
|
||||||
|
if (client_options->client_cert) {
|
||||||
|
if (!ssl_use_pem_file(&fake_ctx, client_options->client_cert)) {
|
||||||
|
mg_snprintf(NULL,
|
||||||
|
NULL, /* No truncation check for ebuf */
|
||||||
|
ebuf,
|
||||||
|
ebuf_len,
|
||||||
|
"Can not use SSL client certificate");
|
||||||
|
SSL_CTX_free(conn->client_ssl_ctx);
|
||||||
|
closesocket(sock);
|
||||||
|
mg_free(conn);
|
||||||
|
conn = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client_options->server_cert) {
|
||||||
|
SSL_CTX_load_verify_locations(conn->client_ssl_ctx,
|
||||||
|
client_options->server_cert,
|
||||||
|
NULL);
|
||||||
|
SSL_CTX_set_verify(conn->client_ssl_ctx, SSL_VERIFY_PEER, NULL);
|
||||||
|
} else {
|
||||||
|
SSL_CTX_set_verify(conn->client_ssl_ctx, SSL_VERIFY_NONE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sslize(conn, conn->client_ssl_ctx, SSL_connect)) {
|
||||||
|
mg_snprintf(NULL,
|
||||||
|
NULL, /* No truncation check for ebuf */
|
||||||
|
ebuf,
|
||||||
|
ebuf_len,
|
||||||
|
"SSL connection error");
|
||||||
|
SSL_CTX_free(conn->client_ssl_ctx);
|
||||||
|
closesocket(sock);
|
||||||
|
mg_free(conn);
|
||||||
|
conn = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -10558,6 +10615,37 @@ mg_connect_client(const char *host,
|
|||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CIVETWEB_API struct mg_connection *
|
||||||
|
mg_connect_client_secure(const struct mg_client_options *client_options,
|
||||||
|
char *error_buffer,
|
||||||
|
size_t error_buffer_size)
|
||||||
|
{
|
||||||
|
return mg_connect_client_impl(client_options,
|
||||||
|
1,
|
||||||
|
error_buffer,
|
||||||
|
error_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct mg_connection *
|
||||||
|
mg_connect_client(const char *host,
|
||||||
|
int port,
|
||||||
|
int use_ssl,
|
||||||
|
char *error_buffer,
|
||||||
|
size_t error_buffer_size)
|
||||||
|
{
|
||||||
|
struct mg_client_options opts;
|
||||||
|
memset(&opts, 0, sizeof(opts));
|
||||||
|
opts.host = host;
|
||||||
|
opts.port = port;
|
||||||
|
return mg_connect_client_impl(&opts,
|
||||||
|
use_ssl,
|
||||||
|
error_buffer,
|
||||||
|
error_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
const char *proto;
|
const char *proto;
|
||||||
size_t proto_len;
|
size_t proto_len;
|
||||||
@@ -10568,6 +10656,7 @@ static const struct {
|
|||||||
{"wss://", 6, 443},
|
{"wss://", 6, 443},
|
||||||
{NULL, 0, 0}};
|
{NULL, 0, 0}};
|
||||||
|
|
||||||
|
|
||||||
/* Check if the uri is valid.
|
/* Check if the uri is valid.
|
||||||
* return 0 for invalid uri,
|
* return 0 for invalid uri,
|
||||||
* return 1 for *,
|
* return 1 for *,
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ civetweb_add_test(PublicServer "Check test environment")
|
|||||||
civetweb_add_test(PublicServer "Start threads")
|
civetweb_add_test(PublicServer "Start threads")
|
||||||
civetweb_add_test(PublicServer "Start Stop HTTP Server")
|
civetweb_add_test(PublicServer "Start Stop HTTP Server")
|
||||||
civetweb_add_test(PublicServer "Start Stop HTTPS Server")
|
civetweb_add_test(PublicServer "Start Stop HTTPS Server")
|
||||||
|
civetweb_add_test(PublicServer "TLS Server Client")
|
||||||
civetweb_add_test(PublicServer "Server Requests")
|
civetweb_add_test(PublicServer "Server Requests")
|
||||||
|
|
||||||
# Tests with main.c
|
# Tests with main.c
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ START_TEST(test_the_test_environment)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
static void *threading_data;
|
static void *threading_data;
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
@@ -186,6 +187,7 @@ START_TEST(test_threading)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
log_msg_func(const struct mg_connection *conn, const char *message)
|
log_msg_func(const struct mg_connection *conn, const char *message)
|
||||||
{
|
{
|
||||||
@@ -271,6 +273,7 @@ START_TEST(test_mg_start_stop_http_server)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST(test_mg_start_stop_https_server)
|
START_TEST(test_mg_start_stop_https_server)
|
||||||
{
|
{
|
||||||
#ifndef NO_SSL
|
#ifndef NO_SSL
|
||||||
@@ -369,6 +372,12 @@ START_TEST(test_mg_server_and_client_tls)
|
|||||||
struct mg_callbacks callbacks;
|
struct mg_callbacks callbacks;
|
||||||
char errmsg[256];
|
char errmsg[256];
|
||||||
|
|
||||||
|
struct mg_connection *client_conn;
|
||||||
|
char client_err[256];
|
||||||
|
const struct mg_request_info *client_ri;
|
||||||
|
int client_res;
|
||||||
|
struct mg_client_options client_options;
|
||||||
|
|
||||||
const char *OPTIONS[32]; /* initializer list here is rejected by CI test */
|
const char *OPTIONS[32]; /* initializer list here is rejected by CI test */
|
||||||
int opt_idx = 0;
|
int opt_idx = 0;
|
||||||
char server_cert[256];
|
char server_cert[256];
|
||||||
@@ -432,6 +441,38 @@ START_TEST(test_mg_server_and_client_tls)
|
|||||||
|
|
||||||
test_sleep(1);
|
test_sleep(1);
|
||||||
|
|
||||||
|
memset(client_err, 0, sizeof(client_err));
|
||||||
|
client_conn =
|
||||||
|
mg_connect_client("127.0.0.1", 8443, 1, client_err, sizeof(client_err));
|
||||||
|
ck_assert(client_conn == NULL);
|
||||||
|
ck_assert_str_ne(client_err, "");
|
||||||
|
|
||||||
|
memset(client_err, 0, sizeof(client_err));
|
||||||
|
memset(&client_options, 0, sizeof(client_options));
|
||||||
|
client_options.host = "127.0.0.1";
|
||||||
|
client_options.port = 8443;
|
||||||
|
client_options.client_cert = client_cert;
|
||||||
|
client_options.server_cert = server_cert;
|
||||||
|
|
||||||
|
client_conn = mg_connect_client_secure(&client_options,
|
||||||
|
client_err,
|
||||||
|
sizeof(client_err));
|
||||||
|
ck_assert(client_conn != NULL);
|
||||||
|
ck_assert_str_eq(client_err, "");
|
||||||
|
mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n");
|
||||||
|
client_res =
|
||||||
|
mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
|
||||||
|
ck_assert_int_ge(client_res, 0);
|
||||||
|
ck_assert_str_eq(client_err, "");
|
||||||
|
client_ri = mg_get_request_info(client_conn);
|
||||||
|
ck_assert(client_ri != NULL);
|
||||||
|
ck_assert_str_eq(client_ri->uri, "200");
|
||||||
|
/* TODO: ck_assert_str_eq(client_ri->request_method, "HTTP/1.0"); */
|
||||||
|
client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
|
||||||
|
ck_assert_int_gt(client_res, 0);
|
||||||
|
ck_assert_int_le(client_res, sizeof(client_err));
|
||||||
|
mg_close_connection(client_conn);
|
||||||
|
|
||||||
/* TODO: A client API using a client certificate is missing */
|
/* TODO: A client API using a client certificate is missing */
|
||||||
|
|
||||||
test_sleep(1);
|
test_sleep(1);
|
||||||
@@ -1238,6 +1279,7 @@ START_TEST(test_request_handlers)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
Suite *
|
Suite *
|
||||||
make_public_server_suite(void)
|
make_public_server_suite(void)
|
||||||
{
|
{
|
||||||
@@ -1247,8 +1289,7 @@ make_public_server_suite(void)
|
|||||||
TCase *const startthreads = tcase_create("Start threads");
|
TCase *const startthreads = tcase_create("Start threads");
|
||||||
TCase *const startstophttp = tcase_create("Start Stop HTTP Server");
|
TCase *const startstophttp = tcase_create("Start Stop HTTP Server");
|
||||||
TCase *const startstophttps = tcase_create("Start Stop HTTPS Server");
|
TCase *const startstophttps = tcase_create("Start Stop HTTPS Server");
|
||||||
TCase *const serverandclienttls =
|
TCase *const serverandclienttls = tcase_create("TLS Server Client");
|
||||||
tcase_create("Start Stop TLS Server Client");
|
|
||||||
TCase *const serverrequests = tcase_create("Server Requests");
|
TCase *const serverrequests = tcase_create("Server Requests");
|
||||||
|
|
||||||
tcase_add_test(checktestenv, test_the_test_environment);
|
tcase_add_test(checktestenv, test_the_test_environment);
|
||||||
|
|||||||
Reference in New Issue
Block a user