Skip to content

Commit 87290a1

Browse files
committed
[mod_openssl] allow separate key/cert files and multiple pemfile options
Change-Id: Ie90e1830cecfebe343321010f6a358a7a8784029
1 parent 36aa0ea commit 87290a1

File tree

2 files changed

+99
-28
lines changed

2 files changed

+99
-28
lines changed

doc/mod_openssl.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
<short>(mandatory) the socket address to listen on (same as "listen":plugin_core.html#plugin_core__setup_listen), can be specified more than once to setup multiple sockets with the same options</short>
1111
</entry>
1212
<entry name="pemfile">
13-
<short>(mandatory) file containing the private key, certificate and (optionally) intermediate certificates (the root certificate is usually not included)</short>
13+
<short>(mandatory) file containing the private key, certificate and (optionally) intermediate certificates (the root certificate is usually not
14+
included); alternatively it can be a key-value list with a "key" and a "cert" entry.</short>
1415
</entry>
1516
<entry name="ca-file">
1617
<short>file containing the intermediate certificates</short>

src/modules/mod_openssl.c

Lines changed: 97 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,86 @@ static gboolean openssl_options_set_string(long *options, GString *s) {
502502

503503
static int openssl_verify_any_cb(int ok, X509_STORE_CTX *ctx) { UNUSED(ok); UNUSED(ctx); return 1; }
504504

505+
static gboolean creds_add_pemfile(liServer *srv, openssl_context *ctx, liValue *pemfile) {
506+
const char *keyfile = NULL;
507+
const char *certfile = NULL;
508+
509+
if (LI_VALUE_STRING == li_value_type(pemfile)) {
510+
keyfile = pemfile->data.string->str;
511+
certfile = pemfile->data.string->str;
512+
} else if (li_value_list_len(pemfile) >= 2) {
513+
if (NULL == (pemfile = li_value_to_key_value_list(pemfile))) {
514+
ERROR(srv, "%s", "openssl expects a hash/key-value list or a string as pemfile parameter");
515+
return FALSE;
516+
}
517+
518+
LI_VALUE_FOREACH(entry, pemfile)
519+
liValue *entryKey = li_value_list_at(entry, 0);
520+
liValue *entryValue = li_value_list_at(entry, 1);
521+
GString *entryKeyStr;
522+
523+
if (LI_VALUE_STRING != li_value_type(entryKey)) {
524+
ERROR(srv, "%s", "openssl pemfile doesn't take default keys");
525+
return FALSE;
526+
}
527+
entryKeyStr = entryKey->data.string; /* keys are either NONE or STRING */
528+
529+
if (g_str_equal(entryKeyStr->str, "key")) {
530+
if (LI_VALUE_STRING != li_value_type(entryValue)) {
531+
ERROR(srv, "%s", "openssl pemfile.key expects a string as parameter");
532+
return FALSE;
533+
}
534+
if (NULL != keyfile) {
535+
ERROR(srv, "openssl unexpected duplicate parameter pemfile %s", entryKeyStr->str);
536+
return FALSE;
537+
}
538+
keyfile = entryValue->data.string->str;
539+
} else if (g_str_equal(entryKeyStr->str, "cert")) {
540+
if (LI_VALUE_STRING != li_value_type(entryValue)) {
541+
ERROR(srv, "%s", "openssl pemfile.cert expects a string as parameter");
542+
return FALSE;
543+
}
544+
if (NULL != certfile) {
545+
ERROR(srv, "openssl unexpected duplicate parameter pemfile %s", entryKeyStr->str);
546+
return FALSE;
547+
}
548+
certfile = entryValue->data.string->str;
549+
} else {
550+
ERROR(srv, "invalid parameter for openssl: pemfile %s", entryKeyStr->str);
551+
return FALSE;
552+
}
553+
LI_VALUE_END_FOREACH()
554+
} else {
555+
ERROR(srv, "%s", "openssl expects a hash/key-value list (with at least \"key\" and \"cert\" entries) or a string as pemfile parameter");
556+
return FALSE;
557+
}
558+
559+
if (NULL == keyfile || NULL == certfile) {
560+
ERROR(srv, "%s", "openssl: missing key or cert in pemfile parameter");
561+
return FALSE;
562+
}
563+
564+
if (SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, certfile) < 0) {
565+
ERROR(srv, "SSL_CTX_use_certificate_chain_file('%s'): %s", certfile,
566+
ERR_error_string(ERR_get_error(), NULL));
567+
return FALSE;
568+
}
569+
570+
if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, keyfile, SSL_FILETYPE_PEM) < 0) {
571+
ERROR(srv, "SSL_CTX_use_PrivateKey_file('%s'): %s", keyfile,
572+
ERR_error_string(ERR_get_error(), NULL));
573+
return FALSE;
574+
}
575+
576+
if (SSL_CTX_check_private_key(ctx->ssl_ctx) != 1) {
577+
ERROR(srv, "SSL: Private key '%s' does not match the certificate public key '%s', reason: %s", keyfile, certfile,
578+
ERR_error_string(ERR_get_error(), NULL));
579+
return FALSE;
580+
}
581+
582+
return TRUE;
583+
}
584+
505585
static gboolean openssl_setup(liServer *srv, liPlugin* p, liValue *val, gpointer userdata) {
506586
openssl_context *ctx;
507587
STACK_OF(X509_NAME) *client_ca_list;
@@ -515,9 +595,10 @@ static gboolean openssl_setup(liServer *srv, liPlugin* p, liValue *val, gpointer
515595
have_verify_parameter = FALSE,
516596
have_verify_depth_parameter = FALSE,
517597
have_verify_any_parameter = FALSE,
518-
have_verify_require_parameter = FALSE;
598+
have_verify_require_parameter = FALSE,
599+
have_pemfile_parameter = FALSE;
519600
const char
520-
*ciphers = NULL, *pemfile = NULL, *ca_file = NULL, *client_ca_file = NULL, *dh_params_file = NULL, *ecdh_curve = NULL;
601+
*ciphers = NULL, *ca_file = NULL, *client_ca_file = NULL, *dh_params_file = NULL, *ecdh_curve = NULL;
521602
long
522603
options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_DH_USE
523604
#ifdef SSL_OP_NO_COMPRESSION
@@ -559,15 +640,7 @@ static gboolean openssl_setup(liServer *srv, liPlugin* p, liValue *val, gpointer
559640
}
560641
have_listen_parameter = TRUE;
561642
} else if (g_str_equal(entryKeyStr->str, "pemfile")) {
562-
if (LI_VALUE_STRING != li_value_type(entryValue)) {
563-
ERROR(srv, "%s", "openssl pemfile expects a string as parameter");
564-
return FALSE;
565-
}
566-
if (NULL != pemfile) {
567-
ERROR(srv, "openssl unexpected duplicate parameter %s", entryKeyStr->str);
568-
return FALSE;
569-
}
570-
pemfile = entryValue->data.string->str;
643+
have_pemfile_parameter = TRUE;
571644
} else if (g_str_equal(entryKeyStr->str, "ca-file")) {
572645
if (LI_VALUE_STRING != li_value_type(entryValue)) {
573646
ERROR(srv, "%s", "openssl ca-file expects a string as parameter");
@@ -698,7 +771,7 @@ static gboolean openssl_setup(liServer *srv, liPlugin* p, liValue *val, gpointer
698771
return FALSE;
699772
}
700773

701-
if (NULL == pemfile) {
774+
if (!have_pemfile_parameter) {
702775
ERROR(srv, "%s", "openssl needs a pemfile");
703776
return FALSE;
704777
}
@@ -736,23 +809,20 @@ static gboolean openssl_setup(liServer *srv, liPlugin* p, liValue *val, gpointer
736809
}
737810
}
738811

739-
if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pemfile, SSL_FILETYPE_PEM) < 0) {
740-
ERROR(srv, "SSL_CTX_use_certificate_file('%s'): %s", pemfile,
741-
ERR_error_string(ERR_get_error(), NULL));
742-
goto error_free_socket;
743-
}
812+
LI_VALUE_FOREACH(entry, val)
813+
liValue *entryKey = li_value_list_at(entry, 0);
814+
liValue *entryValue = li_value_list_at(entry, 1);
815+
GString *entryKeyStr;
744816

745-
if (SSL_CTX_use_PrivateKey_file (ctx->ssl_ctx, pemfile, SSL_FILETYPE_PEM) < 0) {
746-
ERROR(srv, "SSL_CTX_use_PrivateKey_file('%s'): %s", pemfile,
747-
ERR_error_string(ERR_get_error(), NULL));
748-
goto error_free_socket;
749-
}
817+
if (LI_VALUE_STRING != li_value_type(entryKey)) continue;
818+
entryKeyStr = entryKey->data.string; /* keys are either NONE or STRING */
750819

751-
if (SSL_CTX_check_private_key(ctx->ssl_ctx) != 1) {
752-
ERROR(srv, "SSL: Private key '%s' does not match the certificate public key, reason: %s", pemfile,
753-
ERR_error_string(ERR_get_error(), NULL));
754-
goto error_free_socket;
755-
}
820+
if (g_str_equal(entryKeyStr->str, "pemfile")) {
821+
if (!creds_add_pemfile(srv, ctx, entryValue)) {
822+
goto error_free_socket;
823+
}
824+
}
825+
LI_VALUE_END_FOREACH()
756826

757827
if (SSL_CTX_set_session_id_context(ctx->ssl_ctx, CONST_USTR_LEN("lighttpd")) != 1) {
758828
ERROR(srv, "SSL_CTX_set_session_id_context(): %s", ERR_error_string(ERR_get_error(), NULL));

0 commit comments

Comments
 (0)