Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for CRL #150

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/ssl.ml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ end
exception Method_error
exception Context_error
exception Certificate_error of string
exception Crl_error of string
exception Cipher_error
exception Diffie_hellman_error
exception Ec_curve_error
Expand All @@ -149,6 +150,7 @@ let () =
| Method_error -> Some "SSL: Method error"
| Context_error -> Some "SSL: Context error"
| Certificate_error s -> Some ("SSL: Certificate error: " ^ s)
| Crl_error s -> Some ("SSL: CRL error: " ^ s)
| Cipher_error -> Some "SSL: Cipher error"
| Diffie_hellman_error -> Some "SSL: Diffie-Hellman error"
| Ec_curve_error -> Some "SSL: EC curve error"
Expand Down Expand Up @@ -176,6 +178,7 @@ let () =
Callback.register_exception "ssl_exn_method_error" Method_error;
Callback.register_exception "ssl_exn_context_error" Context_error;
Callback.register_exception "ssl_exn_certificate_error" (Certificate_error "");
Callback.register_exception "ssl_exn_crl_error" (Crl_error "");
Callback.register_exception "ssl_exn_cipher_error" Cipher_error;
Callback.register_exception
"ssl_exn_diffie_hellman_error"
Expand Down Expand Up @@ -251,6 +254,12 @@ external add_cert_to_store :
-> unit
= "ocaml_ssl_ctx_add_cert_to_store"

external add_crl_to_store :
context
-> string
-> unit
= "ocaml_ssl_ctx_add_crl_to_store"

external use_certificate :
context
-> string
Expand Down Expand Up @@ -454,6 +463,17 @@ external set_hostflags :
external set_host : socket -> string -> unit = "ocaml_ssl_set1_host"
external set_ip : socket -> string -> unit = "ocaml_ssl_set1_ip"

type x509_check_v_flag =
| X509_v_flag_crl_check
| X509_v_flag_crl_check_all

external set_flags :
context
-> x509_check_v_flag list
-> unit
= "ocaml_ssl_set_flags"


(* Here is the signature of the base communication functions that are
implemented below in two versions *)
module type Ssl_base = sig
Expand Down
21 changes: 21 additions & 0 deletions src/ssl.mli
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ exception Ec_curve_error
exception Certificate_error of string
(** The SSL server certificate could not be initialized. *)

exception Crl_error of string
(** The CRL could not be initialized. *)

exception Private_key_error of string
(** The SSL server private key could not be initialized. *)

Expand Down Expand Up @@ -340,6 +343,10 @@ val add_cert_to_store : context -> string -> unit
(** Add a certificate to the [ctx] trust storage. The value should be contents
of the certificate as string in PEM format. *)

val add_crl_to_store : context -> string -> unit
(** Add a CRL to the [ctx] trust storage. The value should be contents
of the CRL as string in PEM format. *)

val use_certificate : context -> string -> string -> unit
(** [use_certificate ctx cert privkey] makes the context [ctx] use [cert] as *
certificate's file name (in PEM format) and [privkey] as private key file *
Expand Down Expand Up @@ -521,6 +528,20 @@ type x509_check_flag =
(* Specify how a certificate should be matched against the host name *)
val set_hostflags : socket -> x509_check_flag list -> unit


(** Flags to specify certificate verification operation*)
type x509_check_v_flag =
| X509_v_flag_crl_check
| X509_v_flag_crl_check_all


(* Specify how certificate verification operation should be done*)
val set_flags :
context
-> x509_check_v_flag list
-> unit


(* Set the expected host name to be verified. *)
val set_host : socket -> string -> unit

Expand Down
57 changes: 57 additions & 0 deletions src/ssl_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,32 @@ CAMLprim value ocaml_ssl_ctx_add_cert_to_store(value context, value cert) {
CAMLreturn(Val_unit);
}

CAMLprim value ocaml_ssl_ctx_add_crl_to_store(value context, value crl) {
CAMLparam2(context,crl);
SSL_CTX *ctx = Ctx_val(context);
const char *crl_data = String_val(crl);
int crl_data_length = caml_string_length(crl);
char buf[256];
X509_CRL *x509_crl = NULL;
BIO *cbio;

caml_release_runtime_system();
cbio = BIO_new_mem_buf((void*)crl_data, crl_data_length);
x509_crl = PEM_read_bio_X509_CRL(cbio, NULL, 0, NULL);

X509_STORE *store = SSL_CTX_get_cert_store(ctx);

if (NULL == x509_crl || X509_STORE_add_crl(store, x509_crl) <= 0) {
ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
caml_acquire_runtime_system();
caml_raise_with_arg(*caml_named_value("ssl_exn_crl_error"), caml_copy_string(buf));
}

caml_acquire_runtime_system();

CAMLreturn(Val_unit);
}

CAMLprim value ocaml_ssl_ctx_use_certificate(value context, value cert,
value privkey) {
CAMLparam3(context, cert, privkey);
Expand Down Expand Up @@ -1597,6 +1623,37 @@ CAMLprim value ocaml_ssl_set1_ip(value socket, value ip) {
CAMLreturn(Val_unit);
}

CAMLprim value ocaml_ssl_set_flags(value context, value flag_lst) {
CAMLparam2(context, flag_lst);
SSL_CTX *ctx = Ctx_val(context);
unsigned int flags = 0;

while (Is_block(flag_lst)) {
switch (Int_val(Field(flag_lst, 0))) {
case 0:
flags |= X509_V_FLAG_CRL_CHECK;
break;
case 1:
flags |= X509_V_FLAG_CRL_CHECK_ALL;
break;
default:
caml_invalid_argument("flags");
}
flag_lst = Field(flag_lst, 1);
}

caml_release_runtime_system();

X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new();
X509_VERIFY_PARAM_set_flags(param, flags);
SSL_CTX_set1_param(ctx, param);
X509_VERIFY_PARAM_free(param);

caml_acquire_runtime_system();

CAMLreturn(Val_unit);
}

CAMLprim value ocaml_ssl_write(value socket, value buffer, value start,
value length) {
CAMLparam2(socket, buffer);
Expand Down
21 changes: 21 additions & 0 deletions tests/HACKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@
#### Configuration (ca.conf)

```
[ ca ]
default_ca = CA_default

[ CA_default ]
serial = serial
database = index.txt
certificate = ca.pem
crlnumber = crlnumber
private_key = ca.key
default_md = sha256

[ req ]
encrypt_key = no
default_md = sha256
Expand Down Expand Up @@ -104,3 +115,13 @@ basicConstraints=critical,CA:FALSE
extendedKeyUsage=critical,clientAuth
subjectKeyIdentifier = hash
```

## Certificates for CRL testing
```shell
touch index.txt
echo 01 > crlnumber
openssl ca -config ca.conf -gencrl -crldays 3650 -out ca.crl
openssl ca -config ca.conf -gencrl -crlsec 60 -out ca_expired.crl
openssl ca -config ca.conf -revoke server.pem
openssl ca -config ca.conf -gencrl -crldays 3650 -out ca_after_revoke.crl
```
8 changes: 8 additions & 0 deletions tests/ca.crl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-----BEGIN X509 CRL-----
MIIBDTCBlQIBATAKBggqhkjOPQQDAjBWMQswCQYDVQQGEwJVUzETMBEGA1UECAwK
Q2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwEUGlh
ZjELMAkGA1UEAwwCQ0EXDTIzMTEyMjE3NDEzNloXDTMzMTExOTE3NDEzNlqgDjAM
MAoGA1UdFAQDAgEBMAoGCCqGSM49BAMCA2cAMGQCMEH21ywhj6Gfpt0iDxbThC+1
EIuyATdK8qbdRAVZWs/rkdxp0iON9+Z8jQ3T+MifbAIwRW/UWn4ngB+GcvidMZbi
7RaOcLiQQt0xP9jxOpPDJ7pqsP6omirOjNnuAE6a6BRk
-----END X509 CRL-----
9 changes: 9 additions & 0 deletions tests/ca_after_revoke.crl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN X509 CRL-----
MIIBNzCBvgIBATAKBggqhkjOPQQDAjBWMQswCQYDVQQGEwJVUzETMBEGA1UECAwK
Q2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwEUGlh
ZjELMAkGA1UEAwwCQ0EXDTIzMTEyMjE3NDEzNloXDTMzMTExOTE3NDEzNlowJzAl
AhQH3cjuAF78TBwN7o09PgYl0eSDHBcNMjMxMTIyMTc0MTM2WqAOMAwwCgYDVR0U
BAMCAQMwCgYIKoZIzj0EAwIDaAAwZQIxAK12yP85IuXLrsfrkxBaXDgJYiXiNckR
JEl3fjN8m4vOsx8cM7/xyd0j8S7Fsdq19QIwcTmgfFIPCVSNsLa2lBhn9Bl+GBJZ
5471241QRJTsGuhAExlQnsdWa8EQtR1jhaHp
-----END X509 CRL-----
8 changes: 8 additions & 0 deletions tests/ca_expired.crl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-----BEGIN X509 CRL-----
MIIBDTCBlQIBATAKBggqhkjOPQQDAjBWMQswCQYDVQQGEwJVUzETMBEGA1UECAwK
Q2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwEUGlh
ZjELMAkGA1UEAwwCQ0EXDTIzMTEyMjE3NDEzNloXDTIzMTEyMjE3NDIzNlqgDjAM
MAoGA1UdFAQDAgECMAoGCCqGSM49BAMCA2cAMGQCMG9VwsiWplHTTZA9QQQ97NFW
NfKH4GyK/jXYtfhdDpDFvs21yX1nJjlFXsO1AH+2/QIwUv6QBKBePf4YD4oe6Zpt
oGozmIJFJkFNpsHddaDkAFIyleX9rYIR8DUIwr3gVIrp
-----END X509 CRL-----
6 changes: 6 additions & 0 deletions tests/dune
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,9 @@
(modules ssl_io)
(libraries ssl alcotest util)
(deps ca.pem ca.key server.key server.pem))

(test
(name ssl_crls)
(modules ssl_crls)
(libraries ssl alcotest util)
(deps client.pem ca.pem ca.key ca.crl ca_expired.crl ca_after_revoke.crl server.key server.pem))
120 changes: 120 additions & 0 deletions tests/ssl_crls.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
open Alcotest
open Util


let test_crl_no_crl () =
let addr = Unix.ADDR_INET (Unix.inet_addr_of_string "127.0.0.1", 1345) in
Util.server_thread addr None |> ignore;

let context = Ssl.create_context TLSv1_3 Client_context in
let set_default = Ssl.set_default_verify_paths context in
Ssl.set_flags context [Ssl.X509_v_flag_crl_check ; Ssl.X509_v_flag_crl_check_all ];
Ssl.load_verify_locations context "ca.pem" "";
let ssl = Ssl.open_connection_with_context context addr in
let cert = Ssl.get_certificate ssl in
let subject = Ssl.get_subject cert in
let verify_result = Ssl.get_verify_result ssl in
let error_string = Ssl.get_verify_error_string 0 in

Ssl.shutdown_connection ssl;
check bool "set default succeded" true set_default;
check
string
"check certificate"
"/C=US/ST=California/L=San Francisco/O=Ocaml-ssl-server/CN=localhost"
subject;
check int "check verify result" 3 verify_result;
check string "check error string" "ok" error_string

let test_crl_expired () =
let addr = Unix.ADDR_INET (Unix.inet_addr_of_string "127.0.0.1", 1346) in
Util.server_thread addr None |> ignore;

let context = Ssl.create_context TLSv1_3 Client_context in
let set_default = Ssl.set_default_verify_paths context in
Ssl.set_flags context [Ssl.X509_v_flag_crl_check ; Ssl.X509_v_flag_crl_check_all];
Ssl.load_verify_locations context "ca.pem" "";
let crlfile = open_in "ca_expired.crl" in
let crlstring = really_input_string crlfile (in_channel_length crlfile) in
Ssl.add_crl_to_store context crlstring;
let ssl = Ssl.open_connection_with_context context addr in
let cert = Ssl.get_certificate ssl in
let subject = Ssl.get_subject cert in
let verify_result = Ssl.get_verify_result ssl in
let error_string = Ssl.get_verify_error_string 0 in

Ssl.shutdown_connection ssl;
check bool "set default succeded" true set_default;
check
string
"check certificate"
"/C=US/ST=California/L=San Francisco/O=Ocaml-ssl-server/CN=localhost"
subject;
check int "check verify result" 12 verify_result;
check string "check error string" "ok" error_string

let test_crl_valid () =
let addr = Unix.ADDR_INET (Unix.inet_addr_of_string "127.0.0.1", 1347) in
Util.server_thread addr None |> ignore;

let context = Ssl.create_context TLSv1_3 Client_context in
let set_default = Ssl.set_default_verify_paths context in
Ssl.set_flags context [Ssl.X509_v_flag_crl_check ; Ssl.X509_v_flag_crl_check_all ];
Ssl.load_verify_locations context "ca.pem" "";
let crlfile = open_in "ca.crl" in
let crlstring = really_input_string crlfile (in_channel_length crlfile) in
Ssl.add_crl_to_store context crlstring;
let ssl = Ssl.open_connection_with_context context addr in
let cert = Ssl.get_certificate ssl in
let subject = Ssl.get_subject cert in
let verify_result = Ssl.get_verify_result ssl in
let error_string = Ssl.get_verify_error_string 0 in

Ssl.shutdown_connection ssl;
check bool "set default succeded" true set_default;
check
string
"check certificate"
"/C=US/ST=California/L=San Francisco/O=Ocaml-ssl-server/CN=localhost"
subject;
check int "check verify result" 0 verify_result;
check string "check error string" "ok" error_string

let test_crl_revoked () =
let addr = Unix.ADDR_INET (Unix.inet_addr_of_string "127.0.0.1", 1348) in
Util.server_thread addr None |> ignore;

let context = Ssl.create_context TLSv1_3 Client_context in
let set_default = Ssl.set_default_verify_paths context in
Ssl.set_flags context [Ssl.X509_v_flag_crl_check ; Ssl.X509_v_flag_crl_check_all ];
let crlfile = open_in "ca_after_revoke.crl" in
let crlstring = really_input_string crlfile (in_channel_length crlfile) in
Ssl.add_crl_to_store context crlstring;
let ssl = Ssl.open_connection_with_context context addr in
let cert = Ssl.get_certificate ssl in
let subject = Ssl.get_subject cert in
let verify_result = Ssl.get_verify_result ssl in
let error_string = Ssl.get_verify_error_string 0 in

Ssl.shutdown_connection ssl;
check bool "set default succeded" true set_default;
check
string
"check certificate"
"/C=US/ST=California/L=San Francisco/O=Ocaml-ssl-server/CN=localhost"
subject;
check int "check verify result" 20 verify_result;
check string "check error string" "ok" error_string


let () =
Alcotest.run
"Ssl CRL functions"
[ ( "CRLs"
, [
test_case "No CRL provided" `Quick test_crl_no_crl
; test_case "CRL expired" `Quick test_crl_expired
; test_case "CRL valid and certificate valid" `Quick test_crl_valid
; test_case "Certificate revoked" `Quick test_crl_revoked
] )
]