Skip to content

Commit be60d6c

Browse files
committed
multi-cert-standardization
1 parent 074875f commit be60d6c

9 files changed

Lines changed: 165 additions & 93 deletions

File tree

include/libwebsockets/lws-context-vhost.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,9 @@ struct lws_context_creation_info {
607607
/**< VHOST: Client SSL context init: CA certificate filepath or NULL */
608608
const void *client_ssl_ca_mem;
609609
/**< VHOST: Client SSL context init: CA certificate memory buffer or
610-
* NULL... use this to load CA cert from memory instead of file */
610+
* NULL... use this to load CA cert(s) from memory instead of file.
611+
* Supports a single certificate (PEM or DER) or multiple concatenated
612+
* PEM certificates across all TLS backends. */
611613

612614
const char *client_ssl_cipher_list;
613615
/**< VHOST: Client SSL context init: List of valid ciphers to use (eg,

lib/misc/base64-decode.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ lws_b64_decode_stateful(struct lws_b64state *s, const char *in, size_t *in_len,
124124
while (in < end_in && *in && !v) {
125125
v = (unsigned char)*in++;
126126

127-
if (v == '\x0a' || v == '\x0d') {
127+
if (v == '\x0a' || v == '\x0d' || v == ' ' || v == '\t') {
128128
v = 0;
129129
continue;
130130
}
@@ -194,6 +194,11 @@ lws_b64_decode_stateful(struct lws_b64state *s, const char *in, size_t *in_len,
194194

195195
s->done += s->len - 1;
196196
s->len = 0;
197+
198+
if (equals) {
199+
while (in < end_in && (*in == '\x0a' || *in == '\x0d' || *in == ' ' || *in == '\t'))
200+
in++;
201+
}
197202
}
198203

199204
if (out < end_out)

lib/tls/gnutls/gnutls-tls.c

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -247,22 +247,10 @@ lws_tls_client_create_vhost_context(struct lws_vhost *vh,
247247
gnutls_certificate_set_x509_trust_file(vh->tls.ssl_client_ctx->creds,
248248
ca_filepath, GNUTLS_X509_FMT_PEM);
249249
} else if (ca_mem && ca_mem_len) {
250-
lws_filepos_t amount = 0;
251-
uint8_t *up1;
252-
253-
if (lws_tls_alloc_pem_to_der_file(vh->context, NULL, ca_mem,
254-
ca_mem_len, &up1, &amount)) {
255-
lwsl_err("%s: Unable to decode x.509 mem\n", __func__);
250+
if (lws_tls_client_vhost_ca_mem_parse(vh, ca_mem, ca_mem_len)) {
251+
lwsl_err("%s: Unable to load x.509 ca_mem\n", __func__);
256252
return 1;
257253
}
258-
259-
if (lws_tls_client_vhost_extra_cert_mem(vh, up1, (size_t)amount)) {
260-
lwsl_err("%s: add extra cert failed\n", __func__);
261-
lws_free(up1);
262-
return 1;
263-
}
264-
265-
lws_free(up1);
266254
} else {
267255
gnutls_certificate_set_x509_system_trust(vh->tls.ssl_client_ctx->creds);
268256
}

lib/tls/openhitls/openhitls-client.c

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -896,21 +896,10 @@ int lws_tls_client_create_vhost_context(
896896
} else {
897897
lwsl_notice("%s: loading CA from memory (%u bytes)\n", __func__,
898898
ca_mem_len);
899-
if (lws_tls_alloc_pem_to_der_file(vh->context, NULL, ca_mem,
900-
ca_mem_len, &der_buf,
901-
&flen)) {
902-
lwsl_err("%s: Unable to decode x.509 mem\n", __func__);
899+
if (lws_tls_client_vhost_ca_mem_parse(vh, ca_mem, ca_mem_len)) {
900+
lwsl_err("%s: Unable to load x.509 ca_mem\n", __func__);
903901
goto bail_cfg;
904902
}
905-
ret = HITLS_CFG_LoadVerifyBuffer(
906-
config, der_buf, (uint32_t)flen, TLS_PARSE_FORMAT_ASN1);
907-
lws_free_set_NULL(der_buf);
908-
if (ret != HITLS_SUCCESS) {
909-
lwsl_err(
910-
"Unable to load SSL Client certs from "
911-
"ssl_ca_mem -- client ssl isn't going to work\n");
912-
} else
913-
lwsl_info("loaded ssl_ca_mem\n");
914903
}
915904

916905
/* Load client certificate if provided (OpenSSL order: filepath first).

lib/tls/openssl/openssl-client.c

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -674,9 +674,13 @@ lws_tls_client_vhost_extra_cert_mem(struct lws_vhost *vh,
674674

675675
st = SSL_CTX_get_cert_store(vh->tls.ssl_client_ctx);
676676
if (!st) {
677-
lwsl_err("%s: failed to get cert store\n", __func__);
678-
X509_free(x);
679-
return 1;
677+
st = X509_STORE_new();
678+
if (!st) {
679+
lwsl_err("%s: failed to create cert store\n", __func__);
680+
X509_free(x);
681+
return 1;
682+
}
683+
SSL_CTX_set_cert_store(vh->tls.ssl_client_ctx, st);
680684
}
681685

682686
n = X509_STORE_add_cert(st, x);
@@ -968,46 +972,10 @@ lws_tls_client_create_vhost_context(struct lws_vhost *vh,
968972
lwsl_info("loaded ssl_ca_filepath\n");
969973
} else {
970974

971-
lws_filepos_t amount = 0;
972-
const uint8_t *up;
973-
uint8_t *up1;
974-
975-
if (lws_tls_alloc_pem_to_der_file(vh->context, NULL, ca_mem,
976-
ca_mem_len, &up1, &amount)) {
977-
lwsl_err("%s: Unable to decode x.509 mem\n", __func__);
978-
lwsl_hexdump_notice(ca_mem, ca_mem_len);
975+
if (lws_tls_client_vhost_ca_mem_parse(vh, ca_mem, ca_mem_len)) {
976+
lwsl_err("%s: Unable to load x.509 ca_mem\n", __func__);
979977
return 1;
980978
}
981-
982-
up = up1;
983-
#if defined(USE_WOLFSSL)
984-
client_CA = d2i_X509(NULL, &up, (int)amount);
985-
#else
986-
client_CA = d2i_X509(NULL, &up, (long)amount);
987-
#endif
988-
if (!client_CA) {
989-
lwsl_err("%s: d2i_X509 failed\n", __func__);
990-
lwsl_hexdump_notice(up1, (size_t)amount);
991-
lws_tls_err_describe_clear();
992-
} else {
993-
x509_store = X509_STORE_new();
994-
if (!X509_STORE_add_cert(x509_store, client_CA)) {
995-
X509_STORE_free(x509_store);
996-
lwsl_err("Unable to load SSL Client certs from "
997-
"ssl_ca_mem -- client ssl isn't going to "
998-
"work\n");
999-
lws_tls_err_describe_clear();
1000-
} else {
1001-
/* it doesn't increment x509_store ref counter */
1002-
SSL_CTX_set_cert_store(vh->tls.ssl_client_ctx,
1003-
x509_store);
1004-
lwsl_info("loaded ssl_ca_mem\n");
1005-
}
1006-
}
1007-
if (client_CA)
1008-
X509_free(client_CA);
1009-
lws_free(up1);
1010-
// lws_tls_client_vhost_extra_cert_mem(vh, ca_mem, ca_mem_len);
1011979
}
1012980

1013981
/*

lib/tls/private-lib-tls.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,13 @@ lws_tls_check_all_cert_lifetimes(struct lws_context *context);
249249
LWS_VISIBLE int
250250
lws_tls_cert_get_x509_remaining(struct lws_context *context, const char *filepath, int *days_left, int *total_days);
251251

252+
int
253+
lws_tls_client_vhost_extra_cert_mem(struct lws_vhost *vh, const uint8_t *der, size_t len);
254+
255+
int
256+
lws_tls_client_vhost_ca_mem_parse(struct lws_vhost *vh, const void *ca_mem,
257+
unsigned int ca_mem_len);
258+
252259
int
253260
lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename,
254261
const char *inbuf, lws_filepos_t inlen,

lib/tls/tls.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,68 @@ lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename,
597597
return 4;
598598
}
599599

600+
int
601+
lws_tls_client_vhost_ca_mem_parse(struct lws_vhost *vh, const void *ca_mem,
602+
unsigned int ca_mem_len)
603+
{
604+
const char *p = (const char *)ca_mem;
605+
const char *end = p + ca_mem_len;
606+
const char *b, *e;
607+
uint8_t *der;
608+
lws_filepos_t der_len;
609+
int count = 0;
610+
611+
if (!ca_mem || !ca_mem_len)
612+
return 0;
613+
614+
/* If it doesn't start with or contain PEM header, assume raw DER */
615+
b = strstr(p, "-----BEGIN");
616+
if (!b || b >= end) {
617+
return lws_tls_client_vhost_extra_cert_mem(vh, (const uint8_t *)ca_mem,
618+
(size_t)ca_mem_len);
619+
}
620+
621+
/* It's PEM - loop over all certificate blocks in the buffer */
622+
while (p < end) {
623+
b = strstr(p, "-----BEGIN");
624+
if (!b || b >= end)
625+
break;
626+
627+
e = strstr(b, "-----END");
628+
if (!e || e >= end)
629+
break;
630+
631+
/* Advance past "-----END ... -----\n" line */
632+
e += 8;
633+
while (e < end && *e != '\n' && *e != '\0')
634+
e++;
635+
if (e < end && *e == '\n')
636+
e++;
637+
638+
der = NULL;
639+
der_len = 0;
640+
if (!lws_tls_alloc_pem_to_der_file(vh->context, NULL, b,
641+
(lws_filepos_t)lws_ptr_diff_size_t(e, b),
642+
&der, &der_len)) {
643+
if (der && der_len) {
644+
if (!lws_tls_client_vhost_extra_cert_mem(vh, der,
645+
(size_t)der_len))
646+
count++;
647+
lws_free(der);
648+
}
649+
}
650+
651+
p = e;
652+
}
653+
654+
if (!count) {
655+
lwsl_err("%s: Unable to parse any CA certs from ca_mem\n", __func__);
656+
return 1;
657+
}
658+
659+
lwsl_info("%s: loaded %d CA cert(s) from ca_mem\n", __func__, count);
660+
return 0;
661+
}
600662

601663
#endif
602664

minimal-examples-lowlevel/api-tests/api-test-x509/main.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,29 @@ int main(int argc, const char **argv)
8484
goto bail;
8585
}
8686

87+
#if defined(LWS_WITH_CLIENT)
88+
{
89+
char multi_cert[4096];
90+
struct lws_vhost *vh;
91+
struct lws_context_creation_info vinfo;
92+
93+
lws_snprintf(multi_cert, sizeof(multi_cert), "%s%s", test_cert, test_cert);
94+
95+
memset(&vinfo, 0, sizeof(vinfo));
96+
vinfo.vhost_name = "test_multi_ca_mem";
97+
vinfo.client_ssl_ca_mem = multi_cert;
98+
vinfo.client_ssl_ca_mem_len = (unsigned int)strlen(multi_cert);
99+
100+
vh = lws_create_vhost(context, &vinfo);
101+
if (!vh) {
102+
lwsl_err("lws_create_vhost failed for multi-cert ca_mem\n");
103+
ret = 1;
104+
goto bail;
105+
}
106+
lwsl_user("Multi-cert ca_mem vhost creation success\n");
107+
}
108+
#endif
109+
87110
lwsl_user("Success\n");
88111

89112
bail:

plugins/protocol_lws_login/protocol_lws_login.c

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,27 +1304,35 @@ callback_lws_login(struct lws *wsi, enum lws_callback_reasons reason,
13041304
const char *token = lws_spa_get_string(pss->spa, EPN_TOKEN);
13051305
const char *target = lws_spa_get_string(pss->spa, EPN_TARGET);
13061306

1307-
char origin[128];
1308-
if (token && vhd && vhd->auth_server_url && lws_hdr_copy(wsi, origin, sizeof(origin), WSI_TOKEN_ORIGIN) > 0) {
1309-
size_t olen = strlen(origin);
1310-
if (olen == 4 && !strcmp(origin, "null")) {
1311-
char referer[256];
1312-
if (lws_hdr_copy(wsi, referer, sizeof(referer), WSI_TOKEN_HTTP_REFERER) > 0) {
1313-
if (strncmp(referer, vhd->auth_server_url, strlen(vhd->auth_server_url))) {
1314-
lwsl_err("%s: blocking SSO CSRF due to bad Referer %s\n", __func__, referer);
1315-
token = NULL;
1316-
} else {
1317-
lwsl_notice("%s: allowing null origin due to valid Referer\n", __func__);
1318-
}
1319-
} else {
1320-
lwsl_err("%s: blocking SSO CSRF due to null origin with missing Referer\n", __func__);
1307+
char origin[128], referer[256];
1308+
const char *chk_url = NULL;
1309+
int has_origin = lws_hdr_copy(wsi, origin, sizeof(origin), WSI_TOKEN_ORIGIN) > 0;
1310+
int has_referer = lws_hdr_copy(wsi, referer, sizeof(referer), WSI_TOKEN_HTTP_REFERER) > 0;
1311+
1312+
if (has_origin && strcmp(origin, "null")) {
1313+
chk_url = origin;
1314+
} else if (has_referer) {
1315+
chk_url = referer;
1316+
}
1317+
1318+
if (token && vhd && vhd->auth_server_url && chk_url) {
1319+
lws_parse_uri_t *puri_auth = lws_parse_uri_create(vhd->auth_server_url);
1320+
lws_parse_uri_t *puri_chk = lws_parse_uri_create(chk_url);
1321+
1322+
if (puri_auth && puri_chk) {
1323+
if (strcmp(puri_auth->scheme, puri_chk->scheme) ||
1324+
strcasecmp(puri_auth->host, puri_chk->host) ||
1325+
puri_auth->port != puri_chk->port) {
1326+
lwsl_err("%s: blocking SSO CSRF from origin/referer %s (expected %s)\n",
1327+
__func__, chk_url, vhd->auth_server_url);
13211328
token = NULL;
1329+
} else {
1330+
lwsl_notice("%s: allowing SSO request matching auth server origin %s\n",
1331+
__func__, chk_url);
13221332
}
1323-
} else if (strncmp(origin, vhd->auth_server_url, olen) ||
1324-
(vhd->auth_server_url[olen] != '\0' && vhd->auth_server_url[olen] != '/')) {
1325-
lwsl_err("%s: blocking SSO CSRF from origin %s\n", __func__, origin);
1326-
token = NULL; /* Nullify to force failure */
13271333
}
1334+
if (puri_auth) lws_parse_uri_destroy(&puri_auth);
1335+
if (puri_chk) lws_parse_uri_destroy(&puri_chk);
13281336
}
13291337

13301338
if (token && target && vhd) {
@@ -1342,11 +1350,31 @@ callback_lws_login(struct lws *wsi, enum lws_callback_reasons reason,
13421350
if (target && target[0]) {
13431351
if (target[0] == '/' && target[1] != '/') {
13441352
final_target = target;
1345-
} else if (vhd->db) {
1346-
if (auth_verify_redirect_uri(vhd, target)) {
1347-
final_target = target;
1348-
} else {
1349-
lwsl_err("%s: untrusted absolute target %s\n", __func__, target);
1353+
} else {
1354+
lws_parse_uri_t *puri_tgt = lws_parse_uri_create(target);
1355+
if (puri_tgt) {
1356+
char host[128] = "";
1357+
const char *h = NULL;
1358+
if (lws_hdr_copy(wsi, host, sizeof(host), WSI_TOKEN_HOST) > 0)
1359+
h = host;
1360+
#if defined(LWS_ROLE_H2)
1361+
else if (lws_hdr_copy(wsi, host, sizeof(host), WSI_TOKEN_HTTP_COLON_AUTHORITY) > 0)
1362+
h = host;
1363+
#endif
1364+
if (!h) {
1365+
struct lws_vhost *vh = lws_get_vhost(wsi);
1366+
if (vh) h = lws_get_vhost_name(vh);
1367+
}
1368+
if (h && !strcasecmp(puri_tgt->host, h)) {
1369+
final_target = target;
1370+
} else if (vhd->db) {
1371+
if (auth_verify_redirect_uri(vhd, target)) {
1372+
final_target = target;
1373+
} else {
1374+
lwsl_err("%s: untrusted absolute target %s\n", __func__, target);
1375+
}
1376+
}
1377+
lws_parse_uri_destroy(&puri_tgt);
13501378
}
13511379
}
13521380
}

0 commit comments

Comments
 (0)