blob: 09e3031e71c77e5af24c5393bfd26ceee72ed528 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
#include "./pn_test_proactor.hpp"
#include <proton/connection.h>
#include <proton/condition.h>
#include <proton/delivery.h>
#include <proton/link.h>
#include <proton/listener.h>
#include <proton/netaddr.h>
#include <proton/proactor.h>
#include <proton/session.h>
#include <proton/sasl.h>
#include <proton/ssl.h>
#include <proton/transport.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct app_data_t {
const char *amqp_address;
const char *container_id;
pn_ssl_domain_t *server_ssl_domain;
bool connection_succeeded;
bool transport_error;
} app_data_t;
/* Note must be run in the current directory to find certificate files */
#define SSL_FILE(NAME) "ssl-certs/" NAME
#define SSL_PW(NAME) NAME "pw"
/* Windows vs. OpenSSL certificates */
#if defined(_WIN32)
# define CERTIFICATE(NAME) SSL_FILE(NAME "-certificate.p12")
# define SET_CREDENTIALS(DOMAIN, NAME) \
pn_ssl_domain_set_credentials(DOMAIN, SSL_FILE(NAME "-full.p12"), "", SSL_PW(NAME))
#else
# define CERTIFICATE(NAME) SSL_FILE(NAME "-certificate.pem")
# define SET_CREDENTIALS(DOMAIN, NAME) \
pn_ssl_domain_set_credentials(DOMAIN, CERTIFICATE(NAME), SSL_FILE(NAME "-private-key.pem"), SSL_PW(NAME))
#endif
/* Returns true to continue, false if finished */
static bool server_handler(app_data_t* app, pn_event_t* event) {
pn_listener_t *l = pn_event_listener(event);
switch (pn_event_type(event)) {
// Server side
case PN_LISTENER_ACCEPT: {
/* Configure a transport to allow SSL and SASL connections. See ssl_domain setup in main() */
pn_transport_t *t = pn_transport();
pn_transport_set_server(t); /* Must call before pn_sasl() */
pn_sasl_allowed_mechs(pn_sasl(t), "ANONYMOUS");
pn_ssl_init(pn_ssl(t), app->server_ssl_domain, NULL);
pn_listener_accept2(l, NULL, t);
/* Accept only one connection */
pn_listener_close(l);
break;
}
case PN_TRANSPORT_CLOSED:
break;
default: break;
}
return true;
}
static bool client_handler(app_data_t* app, pn_event_t* event) {
switch (pn_event_type(event)) {
// Client side
case PN_CONNECTION_INIT: {
pn_connection_t* c = pn_event_connection(event);
pn_session_t* s = pn_session(pn_event_connection(event));
pn_connection_set_container(c, app->container_id);
pn_connection_open(c);
pn_session_open(s);
{
pn_link_t* l = pn_sender(s, "my_sender");
pn_terminus_set_address(pn_link_target(l), app->amqp_address);
pn_link_open(l);
break;
}
}
case PN_CONNECTION_BOUND: {
break;
}
case PN_CONNECTION_REMOTE_OPEN:
app->connection_succeeded = true;
pn_connection_close(pn_event_connection(event));
break;
case PN_TRANSPORT_ERROR:
app->transport_error = true;
break;
case PN_CONNECTION_REMOTE_CLOSE:
pn_connection_close(pn_event_connection(event));
break;
case PN_SESSION_REMOTE_CLOSE:
pn_connection_close(pn_event_connection(event));
break;
case PN_LINK_REMOTE_CLOSE:
case PN_LINK_REMOTE_DETACH:
pn_connection_close(pn_event_connection(event));
break;
default: break;
}
return true;
}
typedef bool handler_t(app_data_t* app, pn_event_t* event);
void run(pn_proactor_t *p, app_data_t *app, handler_t *shandler, handler_t *chandler) {
/* Loop and handle server/client events */
do {
pn_event_batch_t *events = pn_proactor_wait(p);
pn_event_t *e;
for (e = pn_event_batch_next(events); e; e = pn_event_batch_next(events)) {
if (pn_event_type(e)==PN_PROACTOR_INACTIVE) {
return;
}
if (pn_event_listener(e)) {
if (!shandler(app, e)) {
return;
}
} else {
if (!chandler(app, e)) {
return;
}
}
}
pn_proactor_done(p, events);
} while(true);
}
static void run_until_listener_open(pn_proactor_t* p) {
do {
pn_event_batch_t *events = pn_proactor_wait(p);
pn_event_t *e;
for (e = pn_event_batch_next(events); e; e = pn_event_batch_next(events)) {
if (pn_event_type(e)==PN_LISTENER_OPEN) {
pn_proactor_done(p, events);
return;
}
}
pn_proactor_done(p, events);
} while (true);
}
static void setup_connection(pn_proactor_t* proactor, pn_transport_t* t) {
pn_listener_t* l = pn_listener();
pn_proactor_listen(proactor, l, ":0", 16);
// Don't know port until the listener is open
run_until_listener_open(proactor);
// Construct connect address (from listener port)
const pn_netaddr_t* a = pn_listener_addr(l);
char port[32];
port[0] = ':';
pn_netaddr_host_port(a, NULL, 0, port+1, 31);
INFO("Connecting to " << port);
pn_proactor_connect2(proactor, NULL, t, port);
}
TEST_CASE("ssl certificate verification tests") {
struct app_data_t app = {0};
app.container_id = "ssl-test";
app.amqp_address = "fubar";
pn_test::auto_free<pn_proactor_t, pn_proactor_free> proactor(pn_proactor());
/* Configure server for default SSL */
pn_test::auto_free<pn_ssl_domain_t, pn_ssl_domain_free>
sd(pn_ssl_domain(PN_SSL_MODE_SERVER));
app.server_ssl_domain = sd;
/* Configure a client for SSL */
pn_transport_t *t = pn_transport();
pn_test::auto_free<pn_ssl_domain_t, pn_ssl_domain_free>
cd(pn_ssl_domain(PN_SSL_MODE_CLIENT));
SECTION("Default connections don't verify to self signed server (even with correct name)") {
REQUIRE(SET_CREDENTIALS(sd, "tserver") == 0);
REQUIRE(pn_ssl_init(pn_ssl(t), NULL, NULL) == 0);
REQUIRE(pn_ssl_set_peer_hostname(pn_ssl(t), "test_server") == 0);
setup_connection(proactor, t);
run(proactor, &app, server_handler, client_handler);
CHECK(app.connection_succeeded==false);
CHECK(app.transport_error==true);
}
SECTION("Connections noname verify to self signed cert if cert allowed (even with no name)") {
REQUIRE(SET_CREDENTIALS(sd, "tserver") == 0);
REQUIRE(pn_ssl_domain_set_trusted_ca_db(cd, CERTIFICATE("tserver")) == 0);
REQUIRE(pn_ssl_domain_set_peer_authentication(cd, PN_SSL_VERIFY_PEER, NULL) == 0);
REQUIRE(pn_ssl_init(pn_ssl(t), cd, NULL) == 0);
setup_connection(proactor, t);
run(proactor, &app, server_handler, client_handler);
CHECK(app.connection_succeeded==true);
CHECK(app.transport_error==false);
}
SECTION("Connections don't noname verify to self signed cert without cert allowed") {
REQUIRE(SET_CREDENTIALS(sd, "tserver") == 0);
REQUIRE(pn_ssl_domain_set_peer_authentication(cd, PN_SSL_VERIFY_PEER, NULL) == 0);
REQUIRE(pn_ssl_init(pn_ssl(t), cd, NULL) == 0);
setup_connection(proactor, t);
run(proactor, &app, server_handler, client_handler);
CHECK(app.connection_succeeded==false);
CHECK(app.transport_error==true);
}
SECTION("Connections verify with self signed server if cert allowed") {
REQUIRE(SET_CREDENTIALS(sd, "tserver") == 0);
REQUIRE(pn_ssl_domain_set_trusted_ca_db(cd, CERTIFICATE("tserver")) == 0);
REQUIRE(pn_ssl_domain_set_peer_authentication(cd, PN_SSL_VERIFY_PEER_NAME, NULL) == 0);
REQUIRE(pn_ssl_init(pn_ssl(t), cd, NULL) == 0);
REQUIRE(pn_ssl_set_peer_hostname(pn_ssl(t), "test_server") == 0);
setup_connection(proactor, t);
run(proactor, &app, server_handler, client_handler);
CHECK(app.connection_succeeded==true);
CHECK(app.transport_error==false);
}
SECTION("Anonymous server connections don't verify") {
REQUIRE(pn_ssl_domain_set_trusted_ca_db(cd, CERTIFICATE("tserver")) == 0);
REQUIRE(pn_ssl_domain_set_peer_authentication(cd, PN_SSL_VERIFY_PEER_NAME, NULL) == 0);
REQUIRE(pn_ssl_init(pn_ssl(t), cd, NULL) == 0);
setup_connection(proactor, t);
run(proactor, &app, server_handler, client_handler);
CHECK(app.connection_succeeded==false);
CHECK(app.transport_error==true);
}
SECTION("Anonymous connections connect if anonymous allowed") {
#ifndef _WIN32
REQUIRE(pn_ssl_domain_set_peer_authentication(cd, PN_SSL_ANONYMOUS_PEER, NULL) == 0);
REQUIRE(pn_ssl_init(pn_ssl(t), cd, NULL) == 0);
setup_connection(proactor, t);
run(proactor, &app, server_handler, client_handler);
CHECK(app.connection_succeeded==true);
CHECK(app.transport_error==false);
#else
SUCCEED("Skipped: Windows schannel does not support anonymous connections");
#endif
}
SECTION("Default server (anonymous) doesn't verify against default client (verify peername)") {
app.server_ssl_domain = 0;
REQUIRE(pn_ssl_init(pn_ssl(t), NULL, NULL) == 0);
setup_connection(proactor, t);
run(proactor, &app, server_handler, client_handler);
CHECK(app.connection_succeeded==false);
CHECK(app.transport_error==true);
}
SECTION("Default server (anonymous) connects if anonymous allowed") {
#ifndef _WIN32
app.server_ssl_domain = 0;
REQUIRE(pn_ssl_domain_set_peer_authentication(cd, PN_SSL_ANONYMOUS_PEER, NULL) == 0);
REQUIRE(pn_ssl_init(pn_ssl(t), cd, NULL) == 0);
setup_connection(proactor, t);
run(proactor, &app, server_handler, client_handler);
CHECK(app.connection_succeeded==true);
CHECK(app.transport_error==false);
#else
SUCCEED("Skipped: Windows schannel does not support anonymous connections");
#endif
}
}