1
0
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:
bel
2015-11-01 22:57:55 +01:00
parent 69f222f3d4
commit ada9d9b00b
5 changed files with 192 additions and 47 deletions

View File

@@ -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()

View File

@@ -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

View File

@@ -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 *,

View File

@@ -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

View File

@@ -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);