diff --git a/.gitignore b/.gitignore index 3afd10b0..7eb266ba 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ _build/ main.ml .mirage.config +.merlin mirage-unikernel*opam mirage/*/myocamlbuild.ml mirage/*/*ukvm diff --git a/CHANGES.md b/CHANGES.md index 7d9b37fd..df79f01e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,11 @@ +## 0.9.3 (2019-01-07) + +* tls: do not require client sent ciphersuites to be a proper set + (interoperability with some android devices) +* tls_lwt: delay error from writing to peer while reading, record errors only + if state is active (fixes #347) +* migrate opam file to opam 2.0 format + ## 0.9.2 (2018-08-24) * compatibility with ppx_sexp_conv >v0.11.0 (#381), required for 4.07.0 diff --git a/async/bin/jbuild b/async/bin/jbuild new file mode 100644 index 00000000..2bd44930 --- /dev/null +++ b/async/bin/jbuild @@ -0,0 +1,3 @@ +(executable + ((name thales) + (libraries (cmdliner rresult tls_async x509_async)))) diff --git a/async/bin/thales.ml b/async/bin/thales.ml new file mode 100644 index 00000000..a0645605 --- /dev/null +++ b/async/bin/thales.ml @@ -0,0 +1,435 @@ +let invalid_arg fmt = Fmt.kstrf (fun e -> invalid_arg e) fmt + +let ( <.> ) f g x = f (g x) + +module U = Unix + +type cipher = + [ `AES_128_CBC_SHA + | `AES_128_CBC_SHA256 + | `AES_128_CCM + | `AES_128_GCM_SHA256 + | `AES_256_CBC_SHA + | `AES_256_CBC_SHA256 + | `AES_256_CCM + | `AES_256_GCM_SHA384 + | `_3DES_EDE_CBC_SHA + | `RC4_128_MD5 + | `RC4_128_SHA ] + +let ciphers = + [ ("aes-128-cbc-hmac-sha1", `AES_128_CBC_SHA) + ; ("aes-128-cbc-hmac-sha256", `AES_128_CBC_SHA256) + ; ("aes-128-ccm", `AES_128_CCM) + ; ("aes-128-gcm-hmac-sha256", `AES_128_GCM_SHA256) + ; ("aes-256-cbc-hmac-sha1", `AES_256_CBC_SHA) + ; ("aes-256-cbc-hmac-sha256", `AES_256_CBC_SHA256) + ; ("aes-256-ccm", `AES_256_CCM) + ; ("aes-256-gcm-hmac-sha384", `AES_256_GCM_SHA384) + ; ("3des-ede-cbc-hmac-sha1", `_3DES_EDE_CBC_SHA) + ; ("rc4-128-hmac-md5", `RC4_128_MD5) + ; ("rc4-128-hmac-sha1", `RC4_128_SHA) ] + +let cipher_to_string cipher = + List.find (fun (_, x) -> x = cipher) ciphers |> fst + +let cipher_of_string s = + try List.assoc s ciphers with _ -> invalid_arg "Invalid cipher: %s" s + +let cipher_pp ppf = Fmt.string ppf <.> cipher_to_string + +type algorithm = [`RSA | `DHE_RSA] + +let algorithms = [("rsa", `RSA); ("dhe-rsa", `DHE_RSA)] + +let algorithm_to_string algorithm = + List.find (fun (_, x) -> x = algorithm) algorithms |> fst + +let algorithm_of_string s = + try List.assoc s algorithms with _ -> invalid_arg "Invalid algorithm: %s" s + +let algorithm_pp ppf = Fmt.string ppf <.> algorithm_to_string + +let ciphersuite algorithm cipher : Tls.Ciphersuite.ciphersuite = + match (cipher, algorithm) with + | `AES_128_CBC_SHA, `RSA -> `TLS_RSA_WITH_AES_128_CBC_SHA + | `AES_128_CBC_SHA256, `RSA -> `TLS_RSA_WITH_AES_128_CBC_SHA256 + | `AES_128_CCM, `RSA -> `TLS_RSA_WITH_AES_128_CCM + | `AES_128_GCM_SHA256, `RSA -> `TLS_RSA_WITH_AES_128_GCM_SHA256 + | `AES_256_CBC_SHA, `RSA -> `TLS_RSA_WITH_AES_256_CBC_SHA + | `AES_256_CBC_SHA256, `RSA -> `TLS_RSA_WITH_AES_256_CBC_SHA256 + | `AES_256_CCM, `RSA -> `TLS_RSA_WITH_AES_256_CCM + | `AES_256_GCM_SHA384, `RSA -> `TLS_RSA_WITH_AES_256_GCM_SHA384 + | `_3DES_EDE_CBC_SHA, `RSA -> `TLS_RSA_WITH_3DES_EDE_CBC_SHA + | `RC4_128_MD5, `RSA -> `TLS_RSA_WITH_RC4_128_MD5 + | `RC4_128_SHA, `RSA -> `TLS_RSA_WITH_RC4_128_SHA + | `AES_128_CBC_SHA, `DHE_RSA -> `TLS_DHE_RSA_WITH_AES_128_CBC_SHA + | `AES_128_CBC_SHA256, `DHE_RSA -> `TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + | `AES_128_CCM, `DHE_RSA -> `TLS_DHE_RSA_WITH_AES_128_CCM + | `AES_128_GCM_SHA256, `DHE_RSA -> `TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + | `AES_256_CBC_SHA, `DHE_RSA -> `TLS_DHE_RSA_WITH_AES_256_CBC_SHA + | `AES_256_CBC_SHA256, `DHE_RSA -> `TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + | `AES_256_CCM, `DHE_RSA -> `TLS_DHE_RSA_WITH_AES_256_CCM + | `AES_256_GCM_SHA384, `DHE_RSA -> `TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + | `_3DES_EDE_CBC_SHA, `DHE_RSA -> `TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA + | `RC4_128_MD5, `DHE_RSA | `RC4_128_SHA, `DHE_RSA -> + invalid_arg "Ciphersuite unavailable" + +let split_ciphersuite = function + | `TLS_RSA_WITH_AES_128_CBC_SHA -> (`RSA, `AES_128_CBC_SHA) + | `TLS_RSA_WITH_AES_128_CBC_SHA256 -> (`RSA, `AES_128_CBC_SHA256) + | `TLS_RSA_WITH_AES_128_CCM -> (`RSA, `AES_128_CCM) + | `TLS_RSA_WITH_AES_128_GCM_SHA256 -> (`RSA, `AES_128_GCM_SHA256) + | `TLS_RSA_WITH_AES_256_CBC_SHA -> (`RSA, `AES_256_CBC_SHA) + | `TLS_RSA_WITH_AES_256_CBC_SHA256 -> (`RSA, `AES_256_CBC_SHA256) + | `TLS_RSA_WITH_AES_256_CCM -> (`RSA, `AES_256_CCM) + | `TLS_RSA_WITH_AES_256_GCM_SHA384 -> (`RSA, `AES_256_GCM_SHA384) + | `TLS_RSA_WITH_RC4_128_MD5 -> (`RSA, `RC4_128_MD5) + | `TLS_RSA_WITH_RC4_128_SHA -> (`RSA, `RC4_128_SHA) + | `TLS_RSA_WITH_3DES_EDE_CBC_SHA -> (`RSA, `_3DES_EDE_CBC_SHA) + | `TLS_DHE_RSA_WITH_AES_128_CBC_SHA -> (`RSA, `AES_128_CBC_SHA) + | `TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 -> (`RSA, `AES_128_CBC_SHA256) + | `TLS_DHE_RSA_WITH_AES_128_CCM -> (`RSA, `AES_128_CCM) + | `TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 -> (`RSA, `AES_128_GCM_SHA256) + | `TLS_DHE_RSA_WITH_AES_256_CBC_SHA -> (`RSA, `AES_256_CBC_SHA) + | `TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 -> (`RSA, `AES_256_CBC_SHA256) + | `TLS_DHE_RSA_WITH_AES_256_CCM -> (`RSA, `AES_256_CCM) + | `TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 -> (`RSA, `AES_256_GCM_SHA384) + | `TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA -> (`RSA, `_3DES_EDE_CBC_SHA) + +let versions = + [ ("1.0", Tls.Core.TLS_1_0) + ; ("1.1", Tls.Core.TLS_1_1) + ; ("1.2", Tls.Core.TLS_1_2) ] + +let hashes = + [ ("md5", `MD5) + ; ("sha1", `SHA1) + ; ("sha224", `SHA224) + ; ("sha256", `SHA256) + ; ("sha384", `SHA384) + ; ("sha512", `SHA512) ] + +let hash_to_string hash = List.find (fun (_, x) -> x = hash) hashes |> fst + +let hash_of_string s = + try List.assoc s hashes with _ -> invalid_arg "Invalid hash: %s" s + +let hash_pp ppf = Fmt.string ppf <.> hash_to_string + +type authenticator = + [`Ca_file of Fpath.t | `Ca_dir of Fpath.t | `No_authentication] + +type own_cert = + [ `Multiple of (Fpath.t * Fpath.t) list + | `Multiple_default of (Fpath.t * Fpath.t) * (Fpath.t * Fpath.t) list + | `Single of Fpath.t * Fpath.t ] + +exception Jump of Core.Error.t + +let to_own_cert own_cert : + Tls.Config.own_cert Core.Or_error.t Async.Deferred.t = + let list_of_result_to_result_of_list lst = + try + Ok (List.map (function Error err -> raise (Jump err) | Ok v -> v) lst) + with Jump err -> Error err + in + let open Async in + match own_cert with + | `Multiple_default ((default_cert, default_priv_key), chain) -> + X509_async.private_of_pems ~cert:default_cert ~priv_key:default_priv_key + >>= fun default -> + Deferred.List.map + ~f:(fun (cert, priv_key) -> X509_async.private_of_pems ~cert ~priv_key) + chain + >>| fun chain -> + Core.Or_error.( + list_of_result_to_result_of_list chain + >>= fun chain -> + default >>| fun default -> `Multiple_default (default, chain)) + | `Multiple [(cert, priv_key)] | `Single (cert, priv_key) -> ( + X509_async.private_of_pems ~cert ~priv_key + >>| function Ok v -> Ok (`Single v) | Error _ as err -> err ) + | `Multiple chain -> + Deferred.List.map + ~f:(fun (cert, priv_key) -> X509_async.private_of_pems ~cert ~priv_key) + chain + >>| fun chain -> + Core.Or_error.( + list_of_result_to_result_of_list chain >>| fun chain -> `Multiple chain) + +let on_some f = function + | Some x -> Async.(f x >>| fun x -> Some x) + | None -> Async.return None + +let tracer sexp = Fmt.pr "S> %a.\n%!" Sexplib.Sexp.pp sexp + +let handle callback t peer = + let open Async in + let error exn = Fmt.pr "> return an error: %s." (Printexc.to_string exn); return () in + let process () = + Tls_async.reader_and_writer ~error t + >>> fun (rd, wr, cl) -> callback rd wr cl peer + in + let monitor = Monitor.create ~name:"clients" () in + Scheduler.within ~monitor process ; + Monitor.detach_and_iter_errors monitor ~f:(function + | Tls_async.Tls_alert e -> + Fmt.epr "!> %s.\n%!" (Tls.Packet.alert_type_to_string e) + | Tls_async.Tls_failure e -> + Fmt.epr "!> %s.\n%!" (Tls.Engine.string_of_failure e) + | Tls_async.Tls_close -> Fmt.epr "!> tls connection close.\n%!" + | Unix.Unix_error (e, f, p) -> + Fmt.epr "!> (%s, %s, %s).\n%!" (U.error_message e) f p + | exn -> Fmt.epr "!> %s.\n%!" (Core.Exn.to_string exn) ) + +let run host port config = + let open Async in + let callback rd wr cl _peer = + let rec go () = + Reader.read_line rd + >>= function + | `Ok line -> Fmt.pr "> %s.\n%!" line; Writer.write_line wr line ; go () + | `Eof -> Fmt.pr "> connection closed.\n%!"; Writer.close wr >>= fun () -> cl + in + go () >>= fun () -> return () + in + Unix.Inet_addr.of_string_or_getbyname host + >>= fun host -> + let socket = Socket.create Socket.Type.tcp in + let socket = + Socket.bind_inet ~reuseaddr:true socket + (Socket.Address.Inet.create host ~port) + in + let socket = Socket.listen socket in + Fmt.pr "=> Socket binded.\n%!" ; + let rec loop socket = + Tls_async.accept config socket + >>= function + | Ok (t, peer) -> + handle + (fun rd wr cl peer -> callback rd wr cl peer >>> fun () -> ()) + t peer ; + loop socket + | Error err -> + Fmt.epr "!> %a.\n%!" Core.Error.pp err ; + loop socket + in + loop socket >>= fun () -> return (`Ok ()) + +let main host port reneg certificates authenticator ciphers hashes = + let open Async in + X509_async.authenticator authenticator + >>= fun authenticator -> + on_some to_own_cert certificates + >>= fun certificates -> + match (authenticator, certificates) with + | Error err, _ | _, Some (Error err) -> + return (`Error (false, err)) + | Ok authenticator, Some (Ok certificates) -> + let config = + Tls.Config.server ?ciphers ?hashes ~reneg ~certificates ~authenticator + () + in + run host port config + | Ok authenticator, None -> + let config = + Tls.Config.server ?ciphers ?hashes ~reneg ~authenticator () + in + run host port config + +let check host port reneg certificates ca_file ca_path ciphers hashes = + let authenticator = + match (ca_file, ca_path) with + | Some ca_file, None -> `Ca_file ca_file + | None, Some ca_path -> `Ca_dir ca_path + | None, None -> `No_authentication + | Some _, Some _ -> + `Error (true, Core.Error.of_string "Impossible to load both CA file and CA directory.") + in + match authenticator with + | `Error _ as err -> err + | #authenticator as authenticator -> ( + match + Async.Thread_safe.block_on_async (fun () -> + main host port reneg certificates authenticator ciphers hashes ) + with + | Ok v -> v + | Error exn -> + `Error + ( false + , Core.Error.of_exn ~backtrace:(`This "Got an exception during executation") exn) ) + +open Cmdliner + +let ffile = + let parse path = + if Sys.file_exists path then Fpath.of_string path + else Rresult.R.error_msgf "File %s does not exist" path + in + let pp = Fpath.pp in + Arg.conv ~docv:"" (parse, pp) + +let path = + let parse path = + if Sys.is_directory path then Fpath.of_string path + else Rresult.R.error_msgf "Path %s is not a directory" path + in + let pp = Fpath.pp in + Arg.conv ~docv:"" (parse, pp) + +let ca_file = + let doc = "PEM format file of CA's" in + Arg.(value & opt (some ffile) None & info ["ca-file"] ~doc) + +let ca_path = + let doc = "PEM format directory of CA's" in + Arg.(value & opt (some path) None & info ["ca-path"] ~doc) + +let cipher = + let parse s = + try Rresult.R.ok (cipher_of_string s) with Invalid_argument err -> + Rresult.R.error_msg err + in + let pp = cipher_pp in + Arg.conv (parse, pp) ~docv:"" + +let algorithm = + let parse s = + try Rresult.R.ok (algorithm_of_string s) with Invalid_argument err -> + Rresult.R.error_msg err + in + let pp = algorithm_pp in + Arg.conv (parse, pp) ~docv:"" + +let ciphersuite = + let parse s = + match Astring.String.cut ~sep:":" s with + | None -> Rresult.R.error_msgf "Invalid format of ciphersuite: %s" s + | Some (a, c) -> ( + match (Arg.conv_parser algorithm a, Arg.conv_parser cipher c) with + | (Error _ as err), _ | _, (Error _ as err) -> err + | Ok a, Ok c -> ( + try Rresult.R.ok (ciphersuite a c) with Invalid_argument err -> + Rresult.R.error_msg err ) ) + in + let pp = + Fmt.using split_ciphersuite + (Fmt.pair ~sep:(Fmt.const Fmt.string ":") algorithm_pp cipher_pp) + in + Arg.conv ~docv:":" (parse, pp) + +let hash : Nocrypto.Hash.hash Arg.conv = + let parse s = + try Rresult.R.ok (hash_of_string s) with Invalid_argument err -> + Rresult.R.error_msg err + in + let pp = hash_pp in + Arg.conv ~docv:"" (parse, pp) + +let certchain = + let parse s = + match Astring.String.cut ~sep:":" s with + | Some (cert, priv_key) -> + if Sys.file_exists cert && Sys.file_exists priv_key then + Rresult.R.( + Fpath.of_string cert + >>= fun cert -> + Fpath.of_string priv_key >>= fun priv_key -> return (cert, priv_key)) + else + Rresult.R.error_msgf + "Certificate file or private key file don't exist: %s or %s" cert + priv_key + | None -> Rresult.R.error_msgf "Invalid format of certchain: %s" s + in + let pp = Fmt.pair ~sep:(Fmt.const Fmt.string ":") Fpath.pp Fpath.pp in + Arg.conv ~docv:":" (parse, pp) + +let own_cert = + let pp_certchain = Arg.conv_printer certchain in + let parse_certchain = Arg.conv_parser certchain in + let parse s = + let rest_parse = function + | [] -> Rresult.R.ok (`Multiple []) + | [v] -> Rresult.R.(parse_certchain v >>| fun v -> `Single v) + | own_cert -> + let own_cert = + List.map parse_certchain own_cert + |> List.fold_left + (fun acc -> function Ok v -> v :: acc | Error _ -> acc) + [] + in + Rresult.R.ok (`Multiple own_cert) + in + match Astring.String.cut ~sep:"!" s with + | None -> ( + try rest_parse (Astring.String.cuts ~sep:"," s) with _ -> + Rresult.R.error_msgf "Invalid format of own-cert: %s" s ) + | Some (default, rest) -> ( + let open Rresult.R in + parse_certchain default + >>= fun default -> + ( try rest_parse (Astring.String.cuts ~sep:"," rest) with _ -> + Rresult.R.error_msgf "Invalid format of own-cert: %s" rest ) + >>| function + | `Single rest -> `Multiple_default (default, [rest]) + | `Multiple rest -> `Multiple_default (default, rest) ) + in + let pp ppf = function + | `Multiple lst -> + Fmt.pf ppf "(`Multiple %a)" + Fmt.(list ~sep:(const string ",") pp_certchain) + lst + | `Single v -> Fmt.pf ppf "(`Single %a)" pp_certchain v + | `Multiple_default (v, lst) -> + Fmt.pf ppf "(`Multiple_default %a)" + Fmt.( + pair ~sep:(const string "!") pp_certchain + (list ~sep:(const string ",") pp_certchain)) + (v, lst) + in + Arg.conv (parse, pp) + +let ciphers = + let doc = "Ciphers." in + Arg.(value & opt (some (list ciphersuite)) None & info ["ciphers"] ~doc) + +let hashes = + let doc = "Hashes." in + Arg.(value & opt (some (list hash)) None & info ["hashes"] ~doc) + +let own_cert = + let doc = "Read a certificate chain." in + Arg.(value & opt (some own_cert) None & info ["chain"] ~doc) + +let with_default = + let doc = "First element of certificate chain as default." in + Arg.(value & flag & info ["with-default"] ~doc) + +let reneg = + let doc = "Renegotation." in + Arg.(value & flag & info ["reneg"] ~doc) + +let host = + let doc = "Hostname." in + Arg.(value & opt string "localhost" & info ["h"; "host"] ~doc) + +let port = + let doc = "Port." in + Arg.(value & opt int 43 & info ["p"; "port"] ~doc) + +let ret_with_core_error = function + | `Ok _ as v -> v + | `Help _ as v -> v + | `Error (r, exn) -> `Error (r, Core.Error.to_string_hum exn) + +let cmd = + let doc = "Example to use ocaml-tls with async." in + let exits = Term.default_exits in + ( Term.( + ret + (app (pure ret_with_core_error) + (const check $ host $ port $ reneg $ own_cert $ ca_file $ ca_path $ ciphers $ hashes))) + , Term.info "thales" ~doc ~exits ) + +let () = Term.(exit @@ eval cmd) diff --git a/async/lib/jbuild b/async/lib/jbuild new file mode 100644 index 00000000..76362d44 --- /dev/null +++ b/async/lib/jbuild @@ -0,0 +1,9 @@ +(library + ((name tls_async) + (modules (tls_async)) + (libraries (nocrypto.async core async tls)))) + +(library + ((name x509_async) + (modules (x509_async)) + (libraries (ptime.clock core async fpath fmt nocrypto x509)))) diff --git a/async/lib/tls_async.ml b/async/lib/tls_async.ml new file mode 100644 index 00000000..451c81cc --- /dev/null +++ b/async/lib/tls_async.ml @@ -0,0 +1,408 @@ +module U = Unix +open Core +open Async + +exception Bad_fd + +module Async_cstruct = struct + (* XXX(dinosaure): this module generate from a socket: + - a safe reader and a safe writer without exception leaks + - non-blocking reader/writer iff socket handles it + - semantically, writer writes entirely the buffer (it returns unit) + - semantically, reader informs if socket is close ([`Eof]) or not *) + + let protect ~name ~f socket a = + Monitor.try_with ~name (fun () -> + f a + >>= fun res -> + match Socket.getopt socket Socket.Opt.error with + (* XXX(dinosaure): see [sockopt.c], return 0 if nothing. *) + | 0 -> return (Ok res) + | errno -> + return + (Error (U.Unix_error (Unix.Error.of_system_int ~errno, name, ""))) + ) + >>= function Ok res -> return res | Error exn -> return (Error exn) + + type 'kind buffer = Cstruct.t constraint 'kind = [< `Read | `Write] + + let make_writer fd : [`Write] buffer -> [`Ok of int | `Closed | `Bad_fd | `Exn of exn] Deferred.t = + (* XXX(dinosaure): this code is a part of [faraday] project and benefits on + a non-blocking writer when it's possible. *) + let finish result = + let open Unix.Error in + match result with + | `Ok n -> return (`Ok n) + | `Already_closed -> return `Closed + | `Error (Unix.Unix_error ((EWOULDBLOCK | EAGAIN), _, _)) -> ( + Fd.ready_to fd `Write + >>| function + | `Bad_fd -> `Bad_fd | `Closed -> `Closed | `Ready -> `Ok 0 ) + | `Error (Unix.Unix_error (EBADF, _, _)) -> return `Bad_fd + | `Error exn -> + Deferred.don't_wait_for (Fd.close fd) ; + return (`Exn exn) + in + (* XXX(dinosaure): exception leak. *) + fun {Cstruct.buffer= buf; off= pos; len} -> + if Fd.supports_nonblock fd then + finish + (Fd.syscall fd ~nonblocking:true (fun fd -> + Bigstring.write_assume_fd_is_nonblocking ~pos ~len fd buf )) + else + Fd.syscall_in_thread fd ~name:"writer" (fun fd -> + Bigstring.write ~pos ~len fd buf ) + >>= finish + + let make_reader fd : [`Read] buffer -> [`Ok of int | `Eof | `Bad_fd | `Exn of exn] Deferred.t = + let cstruct_to_bigstring {Cstruct.buffer= buf; off= pos; len} f = + f buf ~pos ~len + in + let rec finish fd buf result = + let open Unix.Error in + match result with + | `Already_closed | `Ok 0 -> return `Eof + (* XXX(dinosaure): not sure to return [`Eof] when syscall returns [0]. *) + | `Ok n -> return (`Ok n) + | `Error (Unix.Unix_error ((EWOULDBLOCK | EAGAIN), _, _)) -> ( + Fd.ready_to fd `Read + >>= function + | `Bad_fd -> return `Bad_fd | `Closed -> return `Eof | `Ready -> go fd buf + (* XXX(dinosaure): ready to read again. *) ) + | `Error (Unix.Unix_error (EBADF, _, _)) -> return `Bad_fd + | `Error exn -> + Deferred.don't_wait_for (Fd.close fd) ; + return (`Exn exn) + (* XXX(dinosaure): exception leak. *) + and go fd buf = + if Fd.supports_nonblock fd then + finish fd buf + @@ Fd.syscall fd ~nonblocking:true + @@ fun fd -> + cstruct_to_bigstring buf + @@ fun buf ~pos ~len -> + Unix.Syscall_result.Int.ok_or_unix_error_exn ~syscall_name:"read" + @@ Bigstring.read_assume_fd_is_nonblocking fd buf ~pos ~len + else + ( Fd.syscall_in_thread fd ~name:"read" + @@ fun fd -> + cstruct_to_bigstring buf + @@ fun buf ~pos ~len -> Bigstring.read fd buf ~pos ~len ) + >>= finish fd buf + in + go fd + + type writer = [`Write] buffer -> (unit, exn) result Deferred.t + + type reader = [`Read] buffer -> ([`Ok of int | `Eof], exn) result Deferred.t + type reader_return = [ `Ok of int | `Eof ] + + (* XXX(dinosaure): at this stage, no exception leaks are possible. *) + + let reader_from_socket socket : reader = + let fd = Socket.fd socket in + (fun buffer -> (protect ~name:"read" ~f:(make_reader fd) socket buffer) >>| function + | Ok #reader_return as v -> v + | Ok `Bad_fd -> Error Bad_fd + | Ok (`Exn exn) -> Error exn + | Error _ as err -> err) + + let writer_from_socket socket : writer = + let fd = Socket.fd socket in + let wr cs = protect ~name:"write" ~f:(make_writer fd) socket cs in + let rec wrf wr = function + | cs when Cstruct.len cs = 0 -> return (Ok ()) + | cs -> ( + wr cs + >>= function + | Ok `Bad_fd -> return (Error Bad_fd) + | Ok (`Exn exn) -> return (Error exn) + | Ok (`Ok n) -> wrf wr (Cstruct.shift cs n) + | Ok `Closed -> return (Ok ()) + | Error _ as err -> return err ) + in + wrf wr +end + +type tracer = Sexplib.Sexp.t -> unit + +type 'addr t = + { socket: ([`Active], 'addr) Socket.t + ; tracer: tracer option + ; recv_buf: Cstruct.t + ; mutable state: state + ; mutable linger: Cstruct.t option } + +and state = [`Active of Tls.Engine.state | `Eof | `Error of exn] + +let tracing t f = + match t.tracer with None -> f () | Some hook -> Tls.Tracing.active ~hook f + +exception Tls_alert of Tls.Packet.alert_type + +exception Tls_failure of Tls.Engine.failure + +exception Tls_close + +let with_some f = function Some x -> f x | None -> return () + +(* XXX(dinosaure): from this wrapper, [`Error] can appear in any case on + [t.state]. we short-cut control-flow by raising exception - but to be safe, + after [rd] or [wr], an [`Error] should raise exception too. *) + +let rd, wr = + let recording_errors ~error safe_computation t cs = + safe_computation t.socket cs + >>= function + | Ok res -> return res + | Error exn -> + t.state <- `Error exn ; + return error + (* we save the error in the state and see what happen next. We return a /safe/ value, + however, any next computation should see [t.state] before to do anything. *) + (* raise exn *) + in + (* exception leaks *) + ( recording_errors ~error:(`Ok 0) Async_cstruct.reader_from_socket + , recording_errors ~error:() Async_cstruct.writer_from_socket ) + +let rec rd_react t : [`Ok of Cstruct.t option | `Eof] Deferred.t = + let handle tls raw = + match tracing t @@ fun () -> Tls.Engine.handle_tls tls raw with + | `Ok (state', `Response resp, `Data data) -> + let state' = + match state' with + | `Ok tls -> `Active tls + | `Eof -> `Eof + | `Alert e -> `Error (Tls_alert e) + in + t.state <- state' ; + (* XXX(dinosaure): at this stage, client would like to close. [tls], in + this situation will send a CLOSE_NOTIFY but the client can already + closed the connection. + + In this situation, try to write something will fail. [async] checks + if a [fd] is closed (see [Fd.syscall]). If we follow the execution + path, we should return an [Ok ()] instead to retrieve an exception. + + This comment is a response to mirleft#388 where [lwt] implementation + needs to deal with this kind of leak. *) + with_some (wr t) resp >>= fun () -> return (`Ok data) + | `Fail (alert, `Response resp) -> + t.state <- `Error (Tls_failure alert) ; + wr t resp >>= fun () -> rd_react t + in + match t.state with + | `Error _ -> return (`Ok None) (* do nothing, but still continue loop *) + | `Eof -> + if not (Fd.is_closed (Socket.fd t.socket)) then + Socket.shutdown t.socket `Receive ; + return `Eof + | `Active _ -> ( + rd t t.recv_buf + >>= fun r -> + match (t.state, r) with + | `Active _, `Eof -> + t.state <- `Eof ; + return `Eof + | `Active tls, `Ok n -> handle tls (Cstruct.sub t.recv_buf 0 n) + | `Error _, _ -> + (* XXX(dinosaure): see [rd], when [Async_cstruct.reader_from_socket] + returns [Error], we set [t.state] to be [`Error] (then, we get this + case) AND we raise exception. *) + return (`Ok None) + | `Eof, _ -> + (* XXX(dinosaure): [`Eof] on [t.state] is a non-sense, [rd] can set + [t.state] only on [`Error]. So, if [t.state = `Eof], we already + computed it before [rd]. *) + assert false ) + +(* XXX(dinosaure): [rd] computes [t] and writes on [buf] decoded data. [linger] + is an intermediate buffer to store only decoded data. *) +let rec rd t buf = + let wr_out res = + let rlen = Cstruct.len res in + let n = min (Cstruct.len buf) rlen in + Cstruct.blit res 0 buf 0 n ; + t.linger <- (if n < rlen then Some (Cstruct.sub res n (rlen - n)) else None) ; + return (`Ok n) + in + match t.linger with + | Some res -> wr_out res + | None -> ( + rd_react t + >>= function + | `Eof -> return `Eof + (* XXX(dinosaure): [async] has a specific semantic where a [0] does not mean + a closed socket. So we need to return [`Eof] or [`Ok n] to notice at the + top if socket is closed or not. *) + | `Ok None -> rd t buf + | `Ok (Some res) -> wr_out res ) + +exception Tls_state_not_ready_to_send + +let wrv t css = + match t.state with + | `Error _ -> return () (* exception leaks *) + | `Eof -> + t.state <- `Error Tls_close ; return () + | `Active tls -> ( + match tracing t @@ fun () -> Tls.Engine.send_application_data tls css with + | Some (tls, data) -> + t.state <- `Active tls ; + wr t data + | None -> + t.state <- `Error Tls_state_not_ready_to_send ; + return () ) + +let wr t cs = wrv t [cs] + +(* XXX(dinosaure): this is a point that should particularly be protected from + concurrent r/w. doing this before a [t] is returned is safe; redoing it + during rekeying is not, as the API client already sees the [t] and can + mistakenly interleave writes while this is in progress. *) +let rec drain_handshake t = + let to_linger t mcs = + match (mcs, t.linger) with + | None, _ -> () + | scs, None -> t.linger <- scs + | Some cs, Some linger -> t.linger <- Some (Cstruct.append linger cs) + in + match t.state with + | `Error _ -> return t + | `Eof -> return t + | `Active tls -> ( + if not (Tls.Engine.handshake_in_progress tls) then return t + else + rd_react t + >>= function + | `Eof -> + t.state <- `Error Tls_close ; + return t + | `Ok cs -> to_linger t cs ; drain_handshake t ) + +exception Tls_can't_renegotiate + +let reneg ?authenticator ?acceptable_cas ?cert ?(drop = true) t = + match t.state with + | `Error _ -> return () + | `Eof -> + t.state <- `Error Tls_close ; return () + | `Active tls -> ( + match + tracing t + @@ fun () -> Tls.Engine.reneg ?authenticator ?acceptable_cas ?cert tls + with + | None -> + t.state <- `Error Tls_can't_renegotiate ; + return () + | Some (tls', buf) -> + if drop then t.linger <- None ; + t.state <- `Active tls' ; + wr t buf + >>= fun () -> + drain_handshake t + >>= fun _t -> + assert (Core.phys_equal t _t) ; + return () ) + +let close t = + match t.state with + | `Active tls -> + let _, buf = tracing t @@ fun () -> Tls.Engine.send_close_notify tls in + t.state <- `Eof ; + wr t buf + | _ -> return () + +let close ~error t = + Monitor.try_with ~name:"close" (fun () -> close t) + >>= (function Ok () -> return () | Error exn -> error exn) + (* XXX(dinosaure): if [Fd.close] raises an exception, it should be catched by + user - not by me. *) + >>| fun () -> + if not (Fd.is_closed (Socket.fd t.socket)) then + Deferred.don't_wait_for (Fd.close (Socket.fd t.socket)) + +let server_of_socket ?tracer config socket = + drain_handshake + {state= `Active (Tls.Engine.server config); socket; recv_buf= Cstruct.create 4096; linger= None; tracer} + +let client_of_socket ?tracer config ?host socket = + let config' = + match host with None -> config | Some host -> Tls.Config.peer config host + in + let tls, init = Tls.Engine.client config' in + let t = {state= `Active tls; socket; recv_buf= Cstruct.create 4096; linger= None; tracer} in + wr t init >>= fun () -> drain_handshake t + +exception Tls_socket_closed + +let accept ?tracer config socket = + Socket.accept socket + >>= function + | `Socket_closed -> return (Or_error.of_exn Tls_socket_closed) + | `Ok (socket', addr) -> ( + Monitor.try_with ~name:"handshake" (fun () -> + server_of_socket ?tracer config socket' >>| fun t -> (t, addr) ) + >>= function + | Ok _ as ok -> return ok + | Error _ as err -> + Socket.shutdown socket' `Both ; + return (Or_error.of_exn_result err) ) + +let connect ?tracer config socket addr = + Monitor.try_with ~name:"connect" (fun () -> + Socket.connect socket addr + >>= fun socket' -> client_of_socket ?tracer config socket' ) + (* TODO: handle host. *) + >>= function + | Ok _ as ok -> return ok + | Error _ as err -> + Socket.shutdown socket `Both ; + return (Or_error.of_exn_result err) + +let read t buffer off len = rd t (Cstruct.of_bigarray ~off ~len buffer) + +let write t buffer off len = wr t (Cstruct.of_bigarray ~off ~len buffer) + +let pipe ~error t = + let b_reader = Cstruct.create 0x8000 in + let rec f_reader writer = + rd t b_reader + >>= function + | `Ok len -> + Pipe.write writer (Cstruct.to_string (Cstruct.sub b_reader 0 len)) + >>= fun () -> f_reader writer + | `Eof -> + (* XXX(dinosaure): if we don't do that, we have an infinite loop. *) + Pipe.close writer ; return () + in + let rec f_writer reader = + Pipe.read reader + >>= function + | `Ok s -> wr t (Cstruct.of_string s) >>= fun () -> f_writer reader + | `Eof -> close ~error t + in + (* XXX(dinosaure): may be we need to close [reader]. *) + ( Pipe.create_reader ~close_on_exception:false f_reader + , Pipe.create_writer f_writer ) + +let reader_and_writer ~error t = + let pr, pw = pipe ~error t in + let info = Info.create "tls" t Sexplib.Conv.sexp_of_opaque in + Reader.of_pipe info pr + >>= fun reader -> + Writer.of_pipe info pw + >>= fun (writer, `Closed_and_flushed_downstream closed) -> + return (reader, writer, closed) + +let epoch t = + match t.state with + | `Active tls -> ( + match Tls.Engine.epoch tls with + | `InitialEpoch -> assert false (* can never occur. *) + | `Epoch data -> `Ok data ) + | `Eof | `Error _ -> `Error + +(* TODO: replace by an async filler. *) +let () = ignore @@ Nocrypto_entropy_async.initialize () diff --git a/async/lib/tls_async.mli b/async/lib/tls_async.mli new file mode 100644 index 00000000..3e514a9e --- /dev/null +++ b/async/lib/tls_async.mli @@ -0,0 +1,81 @@ +open Async +open Core + +type 'addr t constraint 'addr = [< Socket.Address.t] + +type tracer = Sexplib.Sexp.t -> unit + +exception Bad_fd +(** Raised by internal reader/writer when [read] or [write] {i syscall} raises + [EBADF] Unix-exception. *) + +exception Tls_alert of Tls.Packet.alert_type +(** Raised by the TLS reader when it handles input. *) + +exception Tls_failure of Tls.Engine.failure +(** Raised by the TLS reader when it handles input. *) + +exception Tls_close +(** Raise by the TLS state when it retrieves end-of-input state but user want to + write something or renegociate. *) + +exception Tls_state_not_ready_to_send +(** Raised by the TLS state when it not able to send something. *) + +exception Tls_can't_renegotiate +(** Raised by the TLS state when it not able to renegociate. *) + +exception Tls_socket_closed +(** Raised by [accept] when socket is closed. *) + +val server_of_socket : + ?tracer:tracer + -> Tls.Config.server + -> ([`Active], ([< Socket.Address.t] as 'a)) Socket.t + -> 'a t Deferred.t + +val client_of_socket : + ?tracer:tracer + -> Tls.Config.client + -> ?host:string + -> ([`Active], ([< Socket.Address.t] as 'a)) Socket.t + -> 'a t Deferred.t + +(** Low level API. *) + +val accept : + ?tracer:tracer + -> Tls.Config.server + -> ([`Passive], ([< Socket.Address.t] as 'a)) Socket.t + -> ('a t * 'a) Or_error.t Deferred.t + +val connect : + ?tracer:tracer + -> Tls.Config.client + -> ([< `Bound | `Unconnected], ([< Socket.Address.t] as 'a)) Socket.t + -> 'a + -> 'a t Or_error.t Deferred.t + +val read : + 'a t -> Bigstring.t -> int -> int -> [`Eof | `Ok of int] Deferred.t + +val write : 'a t -> Bigstring.t -> int -> int -> unit Deferred.t + +val close : error:(exn -> unit Deferred.t) -> 'a t -> unit Deferred.t + +val reneg : + ?authenticator:X509.Authenticator.a + -> ?acceptable_cas:X509.distinguished_name list + -> ?cert:Tls.Config.own_cert + -> ?drop:bool + -> 'a t + -> unit Deferred.t + +val epoch : 'a t -> [`Ok of Tls.Core.epoch_data | `Error] + +(** High level API. *) + +val reader_and_writer : + error:(exn -> unit Deferred.t) + -> 'a t + -> (Reader.t * Writer.t * unit Deferred.t) Deferred.t diff --git a/async/lib/x509_async.ml b/async/lib/x509_async.ml new file mode 100644 index 00000000..99593929 --- /dev/null +++ b/async/lib/x509_async.ml @@ -0,0 +1,72 @@ +let ( <.> ) f g x = f (g x) + +open Core +open Async + +type priv = X509.t list * Nocrypto.Rsa.priv + +type authenticator = X509.Authenticator.a + +let load_dir path = + Sys.ls_dir (Fpath.to_string path) >>| List.map ~f:Fpath.(( / ) path) + +let load_file path = + Monitor.try_with ~run:`Now (fun () -> + Reader.file_contents (Fpath.to_string path) >>| Cstruct.of_string ) + >>| function + | Ok v -> Ok v + | Error exn -> + Or_error.error (Fmt.strf "Failed to load file %a" Fpath.pp path) exn Exn.sexp_of_t + +let private_of_pems ~cert ~priv_key = + let open X509.Encoding.Pem in + load_file cert + >>| Result.map ~f:Certificate.of_pem_cstruct + >>= fun certs -> + load_file priv_key + >>| Result.map ~f:Private_key.of_pem_cstruct1 + >>| fun pk -> + match (certs, pk) with + | Ok certs, Ok (`RSA pk) -> (Ok (certs, pk)) + | (Error _ as err), Ok _ -> err + | Ok _, (Error _ as err) -> err + | Error err0, Error err1 -> Or_error.both (Error err0) (Error err1) + +let certs_of_pem path = + load_file path >>| Result.map ~f:X509.Encoding.Pem.Certificate.of_pem_cstruct + +let certs_of_pem_dir ?(ext = "crt") path = + load_dir path + >>| List.filter ~f:(Fpath.has_ext ext) + >>= Deferred.List.concat_map ~how:`Parallel ~f:(fun path -> + certs_of_pem path + >>| function + | Ok certs -> certs + | Error err -> + Fmt.epr "Silently got an error when we tried to load %a: %a" + Fpath.pp path Error.pp err ; + [] ) + +let authenticator meth = + let time = Synchronous_time_source.wall_clock () in + let now = + Synchronous_time_source.now time + |> Time_ns.to_span_since_epoch |> Time_ns.Span.to_int_sec + |> Ptime.Span.of_int_s |> Ptime.of_span + |> fun opt -> Option.value_exn ~message:"Invalid time value" opt + in + let of_meth meth = X509.Authenticator.chain_of_trust ~time:now meth + and dotted_hex_to_cs = + Nocrypto.Uncommon.Cs.of_hex + <.> String.map ~f:(function ':' -> ' ' | x -> x) + and fingerprint hash fingerprints = + X509.Authenticator.server_key_fingerprint ~time:now ~hash ~fingerprints + in + match meth with + | `Ca_file path -> certs_of_pem path >>| Result.map ~f:of_meth + | `Ca_dir path -> certs_of_pem_dir path >>| of_meth >>| Result.return + | `Key_fingerprints (hash, fps) -> return (Ok (fingerprint hash fps)) + | `Hex_key_fingerprints (hash, fps) -> + let fps = List.map ~f:(fun (n, v) -> (n, dotted_hex_to_cs v)) fps in + return (Ok (fingerprint hash fps)) + | `No_authentication -> return (Ok X509.Authenticator.null) diff --git a/async/lib/x509_async.mli b/async/lib/x509_async.mli new file mode 100644 index 00000000..f4a2b560 --- /dev/null +++ b/async/lib/x509_async.mli @@ -0,0 +1,35 @@ +open Core +open Async + +(** X.509 certificate handling using Async. *) + +(** Private material: a certificate chain and a RSA private key. *) +type priv = X509.t list * Nocrypto.Rsa.priv + +(** Authenticator. *) +type authenticator = X509.Authenticator.a + +val private_of_pems : + cert:Fpath.t + -> priv_key:Fpath.t + -> priv Or_error.t Deferred.t +(** [private_of_pems ~cert ~priv_key] is [priv], after reading the private key + and certificate chain from the given PEM-encoded files. *) + +val certs_of_pem : Fpath.t -> X509.t list Or_error.t Deferred.t +(** [certs_of_pem file] is [certificates], which are read from the PEM-encoded + [file]. *) + +val certs_of_pem_dir : ?ext:Fpath.ext -> Fpath.t -> X509.t list Deferred.t +(** [certs_of_pem_dir dir] is [certificates], which are read from all + PEM-encoded files in [dir]. *) + +val authenticator : + [ `Ca_file of Fpath.t + | `Ca_dir of Fpath.t + | `Key_fingerprints of Nocrypto.Hash.hash * (string * Cstruct.t) list + | `Hex_key_fingerprints of Nocrypto.Hash.hash * (string * string) list + | `No_authentication ] + -> authenticator Or_error.t Deferred.t +(** [authenticator methods] constructs an [authenticator] using the specified + method and data. *) diff --git a/lib/handshake_common.ml b/lib/handshake_common.ml index 0676bff4..66976365 100644 --- a/lib/handshake_common.ml +++ b/lib/handshake_common.ml @@ -125,8 +125,10 @@ let client_hello_valid ch = not (empty ch.ciphersuites) && + (* android 4.4 and davdroid do not send proper sets! (List_set.is_proper_set ch.ciphersuites) && + *) (* TODO: if ecc ciphersuite, require ellipticcurves and ecpointformats extensions! *) List_set.is_proper_set (extension_types to_client_ext_type ch.extensions) diff --git a/lwt/tls_lwt.ml b/lwt/tls_lwt.ml index 8cff3d36..963b74a5 100644 --- a/lwt/tls_lwt.ml +++ b/lwt/tls_lwt.ml @@ -54,8 +54,9 @@ module Unix = struct let recording_errors op t cs = Lwt.catch (fun () -> op t.fd cs) - (fun exn -> - t.state <- `Error exn ; + (fun exn -> (match t.state with + | `Error _ | `Eof -> () + | `Active _ -> t.state <- `Error exn) ; fail exn) in (recording_errors Lwt_cs.read, recording_errors Lwt_cs.write_full) @@ -82,7 +83,8 @@ module Unix = struct | `Alert a -> `Error (Tls_alert a) in t.state <- state' ; - (resp |> when_some (write_t t)) >>= fun () -> return (`Ok data) + safely (resp |> when_some (write_t t)) >|= fun () -> + `Ok data | `Fail (alert, `Response resp) -> t.state <- `Error (Tls_failure alert) ; diff --git a/opam b/opam index b7afc795..9a7799ff 100644 --- a/opam +++ b/opam @@ -1,7 +1,7 @@ -opam-version: "1.2" +opam-version: "2.0" name: "tls" homepage: "https://github.com/mirleft/ocaml-tls" -dev-repo: "https://github.com/mirleft/ocaml-tls.git" +dev-repo: "git+https://github.com/mirleft/ocaml-tls.git" bug-reports: "https://github.com/mirleft/ocaml-tls/issues" doc: "https://mirleft.github.io/ocaml-tls/doc" author: ["David Kaloper " "Hannes Mehnert "] @@ -12,15 +12,14 @@ build: [ [ "ocaml" "pkg/pkg.ml" "build" "--pinned" "%{pinned}%" "--tests" "false" "--with-lwt" "%{lwt+ptime:installed}%" "--with-mirage" "%{mirage-flow-lwt+mirage-kv-lwt+mirage-clock+ptime:installed}%" ] -] -build-test: [ ["ocaml" "pkg/pkg.ml" "build" "--pinned" "%{pinned}%" "--tests" "true" "--with-lwt" "%{lwt+ptime+astring:installed}%" - "--with-mirage" "%{mirage-flow-lwt+mirage-kv-lwt+mirage-clock+ptime:installed}%" ] - ["ocaml" "pkg/pkg.ml" "test"] + "--with-mirage" "%{mirage-flow-lwt+mirage-kv-lwt+mirage-clock+ptime:installed}%" ] {with-test} + ["ocaml" "pkg/pkg.ml" "test"] {with-test} ] depends: [ + "ocaml" {>= "4.02.2"} "ocamlfind" {build} "ocamlbuild" {build} "topkg" {build} @@ -32,8 +31,8 @@ depends: [ "sexplib" "nocrypto" {>= "0.5.4"} "x509" {>= "0.6.1"} - "cstruct-unix" {test & >= "3.0.0"} - "ounit" {test} + "cstruct-unix" {with-test & >= "3.0.0"} + "ounit" {with-test} ] depopts: [ "lwt" @@ -41,7 +40,7 @@ depopts: [ "mirage-kv-lwt" "mirage-clock" "ptime" - "astring" {test} + "astring" {with-test} ] conflicts: [ "lwt" {<"2.4.8"} @@ -53,4 +52,4 @@ conflicts: [ ] tags: [ "org:mirage"] -available: [ ocaml-version >= "4.02.2" ] +synopsis: "Transport Layer Security purely in OCaml" \ No newline at end of file diff --git a/pkg/pkg.ml b/pkg/pkg.ml index 8ba711fd..47e68095 100755 --- a/pkg/pkg.ml +++ b/pkg/pkg.ml @@ -30,4 +30,5 @@ let () = Pkg.test ~run:false ~cond:lwt "lwt/examples/echo_client_alpn" ; Pkg.test ~run:false ~cond:lwt "lwt/examples/test_server" ; Pkg.test ~run:false ~cond:lwt "lwt/examples/test_client" ; + Pkg.test ~run:false ~cond:lwt "lwt/examples/http_client" ; ] diff --git a/tests/interop-openssl-sclient.sh b/tests/interop-openssl-sclient.sh index 7711a60c..11bb3cdc 100755 --- a/tests/interop-openssl-sclient.sh +++ b/tests/interop-openssl-sclient.sh @@ -43,14 +43,19 @@ testit extra_args="-tls1_2" testit -ciphers="DHE-RSA-AES256-SHA AES256-SHA DHE-RSA-AES128-SHA AES128-SHA EDH-RSA-DES-CBC3-SHA DES-CBC3-SHA RC4-SHA RC4-MD5" +ciphers="DHE-RSA-AES256-SHA AES256-SHA DHE-RSA-AES128-SHA AES128-SHA" +#OpenSSL <1.1.1: +#EDH-RSA-DES-CBC3-SHA DES-CBC3-SHA RC4-SHA RC4-MD5 for i in $ciphers; do - if [ $i != "RC4-MD5" ]; then - extra_args="-cipher $i" - testit - else - echo "not testing RC4-MD5 without tls version (openssl will use SSLv2 which fails)" - fi +# if [ $i != "RC4-MD5" ]; then +# extra_args="-cipher $i" +# testit +# else +# echo "not testing RC4-MD5 without tls version (openssl will use SSLv2 which fails)" +# fi + + extra_args="-cipher $i" + testit extra_args="-tls1 -cipher $i" testit diff --git a/tests/interop-openssl-sserver.sh b/tests/interop-openssl-sserver.sh index 81d03038..f467b9d5 100755 --- a/tests/interop-openssl-sserver.sh +++ b/tests/interop-openssl-sserver.sh @@ -1,6 +1,6 @@ #!/bin/sh -s_server_args="s_server -no_tmp_rsa -quiet -key ../certificates/server.key -cert ../certificates/server.pem -www -dhparam dh.pem " +s_server_args="s_server -quiet -key ../certificates/server.key -cert ../certificates/server.pem -www -dhparam dh.pem " pidfile='/tmp/openssl.pid' @@ -35,7 +35,9 @@ testit extra_args="-tls1_2" testit -ciphers="DHE-RSA-AES256-SHA AES256-SHA DHE-RSA-AES128-SHA AES128-SHA EDH-RSA-DES-CBC3-SHA DES-CBC3-SHA RC4-SHA RC4-MD5" +ciphers="DHE-RSA-AES256-SHA AES256-SHA DHE-RSA-AES128-SHA AES128-SHA" +#OpenSSL <1.1.1: +#EDH-RSA-DES-CBC3-SHA DES-CBC3-SHA RC4-SHA RC4-MD5 for i in $ciphers; do extra_args="-cipher $i" testit