Skip to content

Commit

Permalink
Add function to compare TLS configs for equality (libusual#57)
Browse files Browse the repository at this point in the history
In pgbouncer, we'd like to be able to compare TLS configs for equality
to avoid over-aggressive connection recycling. Since knowledge about the
structure for these configs live here it seems best to implement that
here.

Original PR: pgbouncer/pgbouncer#1157
  • Loading branch information
cosgroveb authored Sep 8, 2024
1 parent d76ad7a commit f24c5ea
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 2 deletions.
16 changes: 16 additions & 0 deletions test/test_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,21 @@ static void test_strerror_r(void *p)
tt_assert(strlen(strerror_r(EINTR, buf, sizeof(buf))) != 0);
end:;
}
/*
* strcmpeq
*/

static void test_strcmpeq(void *ptr)
{
tt_assert(strcmpeq("foo", "foo") == true);
tt_assert(strcmpeq(NULL, NULL) == true);

tt_assert(strcmpeq("foo", "bar") == false);
tt_assert(strcmpeq("foo", NULL) == false);
tt_assert(strcmpeq(NULL, "foo") == false);
end:;
}


/*
* memrchr
Expand Down Expand Up @@ -635,6 +650,7 @@ struct testcase_t string_tests[] = {
{ "strlcat", test_strlcat },
{ "strnlen", test_strnlen },
{ "strerror_r", test_strerror_r },
{ "strcmpeq", test_strcmpeq },
{ "memrchr", test_memrchr },
{ "memmem", test_memmem },
{ "mempbrk", test_mempbrk },
Expand Down
56 changes: 56 additions & 0 deletions test/test_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,59 @@ static const char *run_case(struct Worker *client, struct Worker *server)
#define COMPLEX1 "key=ssl/ca1_complex1.key", "cert=ssl/ca1_complex1.crt"
#define COMPLEX2 "key=ssl/ca2_complex2.key", "cert=ssl/ca2_complex2.crt"

static void test_tls_config_equal(void *z)
{
struct Worker *server = NULL;
struct Worker *server_unchanged = NULL;
struct Worker *server_changed = NULL;

str_check(create_worker(&server, true, SERVER1, NULL), "OK");
str_check(create_worker(&server_unchanged, true, SERVER1, NULL), "OK");
str_check(create_worker(&server_changed, true, SERVER2, NULL), "OK");

tt_assert(tls_config_equal(server->config, server_unchanged->config) == true);
tt_assert(tls_config_equal(server->config, server_changed->config) == false);
end:;
}

static void test_tls_keypair_list_equal(void *z)
{
struct tls_keypair *kp1a, *kp1b, *kp2a, *kp2b;
kp1a = tls_keypair_new();
kp1b = tls_keypair_new();
kp2a = tls_keypair_new();
kp2b = tls_keypair_new();

kp1a->next = kp1b;
kp2a->next = kp2b;

tls_keypair_set_cert_file(kp1a, "ssl/ca1_server1.crt");
tls_keypair_set_cert_file(kp1b, "ssl/ca1_server2.crt");
tls_keypair_set_cert_file(kp2a, "ssl/ca1_server1.crt");
tls_keypair_set_cert_file(kp2b, "ssl/different_ca1_server2.crt");

tt_assert(tls_keypair_list_equal(kp1a, kp2a) == false);
end:;
}

static void test_tls_keypair_list_length(void *z)
{
struct tls_keypair *kp1a, *kp1b, *kp2a;
kp1a = tls_keypair_new();
kp1b = tls_keypair_new();
kp2a = tls_keypair_new();

/* this keypair list is one keypair longer */
kp1a->next = kp1b;

tls_keypair_set_cert_file(kp1a, "ssl/ca1_server1.crt");
tls_keypair_set_cert_file(kp1b, "ssl/ca1_server2.crt");
tls_keypair_set_cert_file(kp2a, "ssl/ca1_server1.crt");

tt_assert(tls_keypair_list_equal(kp1a, kp2a) == false);
end:;
}

static void test_verify(void *z)
{
struct Worker *server = NULL, *client = NULL;
Expand Down Expand Up @@ -1048,6 +1101,9 @@ struct testcase_t tls_tests[] = {
{ "set-mem", test_set_mem },
{ "cipher-nego", test_cipher_nego },
{ "cert-info", test_cert_info },
{ "tls_config_equal", test_tls_config_equal },
{ "tls_keypair_list_equal", test_tls_keypair_list_equal },
{ "tls_keypair_list_length", test_tls_keypair_list_length },
END_OF_TESTCASES,
{ "servername", test_servername },
};
14 changes: 14 additions & 0 deletions usual/string.c
Original file line number Diff line number Diff line change
Expand Up @@ -677,3 +677,17 @@ size_t strnlen(const char *string, size_t maxlen)
}

#endif

/*
* Same as strcmp, but handles NULLs. If both sides are NULL, returns "true".
*/
bool strcmpeq(const char *str_left, const char *str_right)
{
if (str_left == NULL && str_right == NULL)
return true;

if (str_left == NULL || str_right == NULL)
return false;

return strcmp(str_left, str_right) == 0;
}
2 changes: 2 additions & 0 deletions usual/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,6 @@ int asprintf(char **dst_p, const char *fmt, ...) _PRINTF(2, 3);
int vasprintf(char **dst_p, const char *fmt, va_list ap) _PRINTF(2, 0);
#endif

bool strcmpeq(const char *str_left, const char *str_right);

#endif
73 changes: 73 additions & 0 deletions usual/tls/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -742,4 +742,77 @@ tls_close(struct tls *ctx)
return (rv);
}

static bool
tls_mem_equal(char *mem1, char *mem2, size_t len1, size_t len2)
{
if (len1 != len2)
return false;
if (mem1 && mem2 && memcmp(mem1, mem2, len1) != 0)
return false;
return true;
}

static bool
tls_keypair_equal(struct tls_keypair *tkp1, struct tls_keypair *tkp2)
{
if (!strcmpeq(tkp1->cert_file, tkp2->cert_file))
return false;
if (!tls_mem_equal(tkp1->cert_mem, tkp2->cert_mem, tkp1->cert_len, tkp2->cert_len))
return false;
if (!strcmpeq(tkp1->key_file, tkp2->key_file))
return false;
if (!tls_mem_equal(tkp1->key_mem, tkp2->key_mem, tkp1->key_len, tkp2->key_len))
return false;
return true;
}

bool
tls_keypair_list_equal(struct tls_keypair *tkp1, struct tls_keypair *tkp2)
{
for (; tkp1 != NULL && tkp2 != NULL; tkp1 = tkp1->next, tkp2 = tkp2->next) {
if (!tls_keypair_equal(tkp1, tkp2))
return false;
}

return tkp1 == NULL && tkp2 == NULL;
}

bool
tls_config_equal(struct tls_config *tc1, struct tls_config *tc2)
{
if (!strcmpeq(tc1->ca_file, tc2->ca_file))
return false;
if (!strcmpeq(tc1->ca_path, tc2->ca_path))
return false;
if (!tls_mem_equal(tc1->ca_mem, tc2->ca_mem, tc1->ca_len, tc2->ca_len))
return false;
if (!strcmpeq(tc1->ciphers, tc2->ciphers))
return false;
if (tc1->ciphers_server != tc2->ciphers_server)
return false;
if (tc1->dheparams != tc2->dheparams)
return false;
if (tc1->ecdhecurve != tc2->ecdhecurve)
return false;
if (!tls_keypair_list_equal(tc1->keypair, tc2->keypair))
return false;
if (!strcmpeq(tc1->ocsp_file, tc2->ocsp_file))
return false;
if (!tls_mem_equal(tc1->ocsp_mem, tc2->ocsp_mem, tc1->ocsp_len, tc2->ocsp_len))
return false;
if (tc1->protocols != tc2->protocols)
return false;
if (tc1->verify_cert != tc2->verify_cert)
return false;
if (tc1->verify_client != tc2->verify_client)
return false;
if (tc1->verify_depth != tc2->verify_depth)
return false;
if (tc1->verify_name != tc2->verify_name)
return false;
if (tc1->verify_time != tc2->verify_time)
return false;
return true;
}

#endif /* USUAL_LIBSSL_FOR_TLS */
1 change: 1 addition & 0 deletions usual/tls/tls.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ int tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p, struct tls_config
char **ocsp_url, void **request_blob, size_t *request_size);

int tls_ocsp_process_response(struct tls *ctx, const void *response_blob, size_t size);
bool tls_config_equal(struct tls_config *server_connect_conf_left, struct tls_config *server_connect_conf_right);

#ifdef __cplusplus
}
Expand Down
4 changes: 2 additions & 2 deletions usual/tls/tls_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ set_mem(char **dest, size_t *destlen, const void *src, size_t srclen)
return 0;
}

static struct tls_keypair *
struct tls_keypair *
tls_keypair_new(void)
{
return calloc(1, sizeof(struct tls_keypair));
}

static int
int
tls_keypair_set_cert_file(struct tls_keypair *keypair, const char *cert_file)
{
return set_string(&keypair->cert_file, cert_file);
Expand Down
4 changes: 4 additions & 0 deletions usual/tls/tls_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,8 @@ int tls_asn1_parse_time(struct tls *ctx, const ASN1_TIME *asn1time, time_t *dst)

int asn1_time_parse(const char *, size_t, struct tm *, int);

struct tls_keypair * tls_keypair_new(void);
int tls_keypair_set_cert_file(struct tls_keypair *keypair, const char *cert_file);
bool tls_keypair_list_equal(struct tls_keypair *tkp1, struct tls_keypair *tkp2);

#endif /* HEADER_TLS_INTERNAL_H */

0 comments on commit f24c5ea

Please sign in to comment.