123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793(* Copyright (C) 2003-2005 Samuel Mimram
This file is part of Ocaml-ssl.
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2.1 of the License, or (at your option)
any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *)typeversion={major:int(** major version *);minor:int(** minor version *);patch:int(** patch number (fix + patch in version < 3.0) *)}externalget_version:unit->version="ocaml_ssl_get_version"letnative_library_version:version=get_version()typeprotocol=|SSLv23|SSLv3|TLSv1|TLSv1_1|TLSv1_2|TLSv1_3typecontexttypecertificatetypesockettypessl_error=|Error_none|Error_ssl|Error_want_read|Error_want_write|Error_want_x509_lookup|Error_syscall|Error_zero_return|Error_want_connect|Error_want_accept|Error_want_async|Error_want_async_job|Error_want_client_hello_cb|Error_want_retry_verifytypeverify_error=|Error_v_unable_to_get_issuer_cert|Error_v_unable_to_get_ctl|Error_v_unable_to_decrypt_cert_signature|Error_v_unable_to_decrypt_CRL_signature|Error_v_unable_to_decode_issuer_public_key|Error_v_cert_signature_failure|Error_v_CRL_signature_failure|Error_v_cert_not_yet_valid|Error_v_cert_has_expired|Error_v_CRL_not_yet_valid|Error_v_CRL_has_expired|Error_v_error_in_cert_not_before_field|Error_v_error_in_cert_not_after_field|Error_v_error_in_CRL_last_update_field|Error_v_error_in_CRL_next_update_field|Error_v_out_of_mem|Error_v_depth_zero_self_signed_cert|Error_v_self_signed_cert_in_chain|Error_v_unable_to_get_issuer_cert_locally|Error_v_unable_to_verify_leaf_signature|Error_v_cert_chain_too_long|Error_v_cert_revoked|Error_v_invalid_CA|Error_v_path_length_exceeded|Error_v_invalid_purpose|Error_v_cert_untrusted|Error_v_cert_rejected|Error_v_subject_issuer_mismatch|Error_v_akid_skid_mismatch|Error_v_akid_issuer_serial_mismatch|Error_v_keyusage_no_certsign|Error_v_application_verificationtypebigarray=(char,Bigarray.int8_unsigned_elt,Bigarray.c_layout)Bigarray.Array1.texternalget_error_string:unit->string="ocaml_ssl_get_error_string"(** Kept for backwards compatibility *)moduleError=structtypet=private{library_number:int;reason_code:int;lib:stringoption;reason:stringoption}typeerr_function=|Get_error|Peek_error|Peek_last_errorexternalerror_struct:err_function->t="ocaml_ssl_error_struct"letget_error()=error_structGet_errorletpeek_error()=error_structPeek_errorletpeek_last_error()=error_structPeek_last_error(** Reproduces the string format from ERR_error_string_n *)letpeek_last_error_string()=leterr=peek_last_error()inletlibstring=matcherr.libwithSomelib->lib|None->"lib(0)"inletreasonstring=matcherr.reasonwithSomereason->reason|None->"reason(0)"inPrintf.sprintf"error:%02lX:%06lX:%s::%s"(Int32.of_interr.library_number)(Int32.of_interr.reason_code)libstringreasonstringendexceptionMethod_errorexceptionContext_errorexceptionCertificate_errorofstringexceptionCipher_errorexceptionDiffie_hellman_errorexceptionEc_curve_errorexceptionPrivate_key_errorofstringexceptionUnmatching_keysexceptionInvalid_socketexceptionHandler_errorexceptionConnection_errorofssl_errorexceptionAccept_errorofssl_errorexceptionRead_errorofssl_errorexceptionWrite_errorofssl_errorexceptionVerify_errorofverify_errorexceptionFlush_errorofbool(* true means retry *)let()=Printexc.register_printer(function|Method_error->Some"SSL: Method error"|Context_error->Some"SSL: Context error"|Certificate_errors->Some("SSL: Certificate error: "^s)|Cipher_error->Some"SSL: Cipher error"|Diffie_hellman_error->Some"SSL: Diffie-Hellman error"|Ec_curve_error->Some"SSL: EC curve error"|Private_key_errors->Some("SSL: Private key error: "^s)|Unmatching_keys->Some"SSL: Unmatching keys"|Invalid_socket->Some"SSL: Invalid socket"|Handler_error->Some"SSL: Handler error"|Connection_error_->Some("SSL connection() error: "^Error.peek_last_error_string())|Accept_error_->Some("SSL accept() error: "^Error.peek_last_error_string())|Read_error_->Some("SSL read() error: "^Error.peek_last_error_string())|Write_error_->Some("SSL write() error: "^Error.peek_last_error_string())|Verify_error_->Some("SSL verify() error: "^Error.peek_last_error_string())|Flush_errorb->Some(Printf.sprintf"SSL flush(%b) error: "b^Error.peek_last_error_string())|_->None)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_cipher_error"Cipher_error;Callback.register_exception"ssl_exn_diffie_hellman_error"Diffie_hellman_error;Callback.register_exception"ssl_exn_ec_curve_error"Ec_curve_error;Callback.register_exception"ssl_exn_private_key_error"(Private_key_error"");Callback.register_exception"ssl_exn_unmatching_keys"Unmatching_keys;Callback.register_exception"ssl_exn_invalid_socket"Invalid_socket;Callback.register_exception"ssl_exn_handler_error"Handler_error;Callback.register_exception"ssl_exn_connection_error"(Connection_errorError_none);Callback.register_exception"ssl_exn_accept_error"(Accept_errorError_none);Callback.register_exception"ssl_exn_read_error"(Read_errorError_none);Callback.register_exception"ssl_exn_write_error"(Write_errorError_none);Callback.register_exception"ssl_exn_verify_error"(Verify_errorError_v_application_verification);Callback.register_exception"ssl_exn_flush_error"(Flush_errortrue)letthread_safe=reffalseexternalinit:bool->unit="ocaml_ssl_init"letts=thread_safeletinit?thread_safe()=letthread_safe=matchthread_safewithSomeb->b|None->!tsininitthread_safetypecontext_type=|Client_context|Server_context|Both_contextexternalcreate_context:protocol->context_type->context="ocaml_ssl_create_context"externalset_min_protocol_version:context->protocol->unit="ocaml_ssl_ctx_set_min_proto_version"externalset_max_protocol_version:context->protocol->unit="ocaml_ssl_ctx_set_max_proto_version"externalget_min_protocol_version:context->protocol="ocaml_ssl_ctx_get_min_proto_version"externalget_max_protocol_version:context->protocol="ocaml_ssl_ctx_get_max_proto_version"externaladd_extra_chain_cert:context->string->unit="ocaml_ssl_ctx_add_extra_chain_cert"externaladd_cert_to_store:context->string->unit="ocaml_ssl_ctx_add_cert_to_store"externaluse_certificate:context->string->string->unit="ocaml_ssl_ctx_use_certificate"externaluse_certificate_from_string:context->string->string->unit="ocaml_ssl_ctx_use_certificate_from_string"externalset_password_callback:context->(bool->string)->unit="ocaml_ssl_ctx_set_default_passwd_cb"externalembed_socket:Unix.file_descr->context->socket="ocaml_ssl_embed_socket"externaldisable_protocols:context->protocollist->unit="ocaml_ssl_disable_protocols"externalset_cipher_list:context->string->unit="ocaml_ssl_ctx_set_cipher_list"externalhonor_cipher_order:context->unit="ocaml_ssl_ctx_honor_cipher_order"externalinit_dh_from_file:context->string->unit="ocaml_ssl_ctx_init_dh_from_file"externalinit_ec_from_named_curve:context->string->unit="ocaml_ssl_ctx_init_ec_from_named_curve"externalload_verify_locations:context->string->string->unit="ocaml_ssl_ctx_load_verify_locations"externalset_default_verify_paths:context->bool="ocaml_ssl_ctx_set_default_verify_paths"externalget_verify_result:socket->int="ocaml_ssl_get_verify_result"externalget_verify_error_string:int->string="ocaml_ssl_get_verify_error_string"externaldigest:[`SHA1|`SHA256|`SHA384]->certificate->string="ocaml_ssl_digest"typeverify_mode=|Verify_peer|Verify_fail_if_no_peer_cert|Verify_client_oncetypeverify_callbackexternalget_client_verify_callback_ptr:unit->verify_callback="ocaml_ssl_get_client_verify_callback_ptr"letclient_verify_callback=get_client_verify_callback_ptr()externalset_client_verify_callback_verbose:bool->unit="ocaml_ssl_set_client_verify_callback_verbose"externalset_verify:context->verify_modelist->verify_callbackoption->unit="ocaml_ssl_ctx_set_verify"externalset_verify_depth:context->int->unit="ocaml_ssl_ctx_set_verify_depth"externalset_client_CA_list_from_file:context->string->unit="ocaml_ssl_ctx_set_client_CA_list_from_file"externalset_context_alpn_protos:context->stringlist->unit="ocaml_ssl_ctx_set_alpn_protos"externalset_context_alpn_select_callback:context->(stringlist->stringoption)->unit="ocaml_ssl_ctx_set_alpn_select_callback"externalversion:socket->protocol="ocaml_ssl_version"typecipherexternalget_cipher:socket->cipher="ocaml_ssl_get_current_cipher"externalget_cipher_description:cipher->string="ocaml_ssl_get_cipher_description"(* TODO: get_cipher_bits *)externalget_cipher_name:cipher->string="ocaml_ssl_get_cipher_name"externalget_cipher_version:cipher->string="ocaml_ssl_get_cipher_version"externalget_certificate:socket->certificate="ocaml_ssl_get_certificate"externalread_certificate:string->certificate="ocaml_ssl_read_certificate"externalwrite_certificate:string->certificate->unit="ocaml_ssl_write_certificate"externalget_issuer:certificate->string="ocaml_ssl_get_issuer"externalget_subject:certificate->string="ocaml_ssl_get_subject"externalget_start_date:certificate->Unix.tm="ocaml_ssl_get_start_date"externalget_expiration_date:certificate->Unix.tm="ocaml_ssl_get_expiration_date"externalfile_descr_of_socket:socket->Unix.file_descr="ocaml_ssl_get_file_descr"externalset_client_SNI_hostname:socket->string->unit="ocaml_ssl_set_client_SNI_hostname"externalset_alpn_protos:socket->stringlist->unit="ocaml_ssl_set_alpn_protos"externalget_negotiated_alpn_protocol:socket->stringoption="ocaml_ssl_get_negotiated_alpn_protocol"externalverify:socket->unit="ocaml_ssl_verify"typex509_check_flag=|Always_check_subject|No_wildcards|No_partial_wildcards|Multi_label_wildcards|Single_label_subdomainsexternalset_hostflags:socket->x509_check_flaglist->unit="ocaml_ssl_set_hostflags"externalset_host:socket->string->unit="ocaml_ssl_set1_host"externalset_ip:socket->string->unit="ocaml_ssl_set1_ip"(* Here is the signature of the base communication functions that are
implemented below in two versions *)moduletypeSsl_base=sigvalconnect:socket->unitvalaccept:socket->unitvalssl_shutdown:socket->boolvalflush:socket->unitvalread:socket->Bytes.t->int->int->intvalread_into_bigarray:socket->bigarray->int->int->intvalwrite:socket->Bytes.t->int->int->intvalwrite_substring:socket->string->int->int->intvalwrite_bigarray:socket->bigarray->int->int->intend(* Provide the base implementation communication functions that release the
OCaml runtime lock, allowing multiple systhreads to execute concurrently. *)moduleRuntime_unlock_base=structexternalconnect:socket->unit="ocaml_ssl_connect"externalaccept:socket->unit="ocaml_ssl_accept"externalwrite:socket->Bytes.t->int->int->int="ocaml_ssl_write"externalwrite_substring:socket->string->int->int->int="ocaml_ssl_write"externalwrite_bigarray:socket->bigarray->int->int->int="ocaml_ssl_write_bigarray"externalread:socket->Bytes.t->int->int->int="ocaml_ssl_read"externalread_into_bigarray:socket->bigarray->int->int->int="ocaml_ssl_read_into_bigarray"externalflush:socket->unit="ocaml_ssl_flush"externalssl_shutdown:socket->bool="ocaml_ssl_shutdown"end(* Same as above, but doesn't release the lock. *)moduleRuntime_lock_base=structexternalget_error:socket->int->ssl_error="ocaml_ssl_get_error_code"[@@noalloc]externalconnect:socket->int="ocaml_ssl_connect_blocking"[@@noalloc]letconnectsocket=letret=connectsocketin(* From https://www.openssl.org/docs/man1.1.1/man3/SSL_connect.html:
RETURN VALUES
0 The TLS/SSL handshake was not successful [...]. Call SSL_get_error()
with the return value ret to find out the reason.
1 The TLS/SSL handshake was successfully completed [...].
<0 The TLS/SSL handshake was not successful [...]. Call SSL_get_error()
with the return value ret to find out the reason. *)ifret<>1thenleterr=get_errorsocketretinraise(Connection_errorerr)externalaccept:socket->int="ocaml_ssl_accept_blocking"[@@noalloc]letacceptsocket=letret=acceptsocketin(* From https://www.openssl.org/docs/man1.1.1/man3/SSL_accept.html:
RETURN VALUES
0 The TLS/SSL handshake was not successful [...]. Call SSL_get_error()
with the return value ret to find out the reason.
1 The TLS/SSL handshake was successfully completed [...].
<0 The TLS/SSL handshake was not successful [...]. Call SSL_get_error()
with the return value ret to find out the reason. *)ifret<>1thenleterr=get_errorsocketretinraise(Accept_errorerr)externalwrite:socket->Bytes.t->int->int->int="ocaml_ssl_write_blocking"[@@noalloc]letwritesocketbufferstartlength=ifstart<0theninvalid_arg"Ssl.write: start negative";iflength<0theninvalid_arg"Ssl.write: length negative";ifstart+length>Bytes.lengthbuffertheninvalid_arg"Ssl.write: Buffer too short";letret=writesocketbufferstartlengthin(* From https://www.openssl.org/docs/man1.1.1/man3/SSL_write.html:
RETURN VALUES
> 0 The write operation was successful, the return value is the number of
bytes actually written to the TLS/SSL connection.
<= 0 The write operation was not successful [...]. Call SSL_get_error()
with the return value ret to find out the reason. *)(ifret<=0thenleterr=get_errorsocketretinraise(Write_errorerr));retexternalwrite_substring:socket->string->int->int->int="ocaml_ssl_write_blocking"[@@noalloc]letwrite_substringsocketbufferstartlength=ifstart<0theninvalid_arg"Ssl.write_substring: start negative";iflength<0theninvalid_arg"Ssl.write_substring: length negative";ifstart+length>String.lengthbuffertheninvalid_arg"Ssl.write_substring: Buffer too short";letret=write_substringsocketbufferstartlengthin(ifret<=0thenleterr=get_errorsocketretinraise(Write_errorerr));retexternalwrite_bigarray:socket->bigarray->int->int->int="ocaml_ssl_write_bigarray_blocking"[@@noalloc]letwrite_bigarraysocketbufferstartlength=ifstart<0theninvalid_arg"Ssl.write_bigarray: start negative";iflength<0theninvalid_arg"Ssl.write_bigarray: length negative";ifstart+length>Bigarray.Array1.dimbuffertheninvalid_arg"Ssl.write_bigarray: Buffer too short";letret=write_bigarraysocketbufferstartlengthin(ifret<=0thenleterr=get_errorsocketretinraise(Write_errorerr));retexternalread:socket->Bytes.t->int->int->int="ocaml_ssl_read_blocking"[@@noalloc]letreadsocketbufferstartlength=ifstart<0theninvalid_arg"Ssl.read: start negative";iflength<0theninvalid_arg"Ssl.read: length negative";ifstart+length>Bytes.lengthbuffertheninvalid_arg"Buffer too short";letret=readsocketbufferstartlengthin(* From https://www.openssl.org/docs/man1.1.1/man3/SSL_read.html
RETURN VALUES
> 0 The read operation was successful. The return value is the number of
bytes actually read from the TLS/SSL connection.
<= 0 The read operation was not successful [...]. Call SSL_get_error(3)
with the return value ret to find out the reason. *)(ifret<=0thenleterr=get_errorsocketretinraise(Read_errorerr));retexternalread_into_bigarray:socket->bigarray->int->int->int="ocaml_ssl_read_into_bigarray_blocking"[@@noalloc]letread_into_bigarraysocketbufferstartlength=ifstart<0theninvalid_arg"Ssl.read_into_big_array: start negative";iflength<0theninvalid_arg"Ssl.read_into_big_array: length negative";ifstart+length>Bigarray.Array1.dimbuffertheninvalid_arg"Buffer too short";letret=read_into_bigarraysocketbufferstartlengthin(ifret<=0thenleterr=get_errorsocketretinraise(Read_errorerr));retexternalflush:socket->int="ocaml_ssl_flush_blocking"[@@noalloc]letflushsocket=letret=flushsocketin(* From https://www.openssl.org/docs/man1.1.1/man3/BIO_flush.html:
RETURN VALUES
BIO_flush() returns 1 for success and 0 or -1 for failure.
Additionally, we use -2 to signal the need to retry without allocation,
see [ssl_stubs.c]. *)ifret<>1thenraise(Flush_error(ret=-2))externalssl_shutdown:socket->int="ocaml_ssl_shutdown_blocking"[@@noalloc]letssl_shutdownsocket=letret=ssl_shutdownsocketin(ifret<0thenleterr=get_errorsocketretinraise(Connection_errorerr));ret=1end(* The functor implementing communication functions from a structure of type
Ssl_base *)moduleMake(Ssl_base:Ssl_base)=structincludeSsl_baseletopen_connection_with_contextcontextsockaddr=letdomain=Unix.domain_of_sockaddrsockaddrinletsock=Unix.socketdomainUnix.SOCK_STREAM0intryUnix.connectsocksockaddr;letssl=embed_socketsockcontextinconnectssl;sslwith|exn->Unix.closesock;raiseexnletopen_connectionssl_methodsockaddr=open_connection_with_context(create_contextssl_methodClient_context)sockaddrletclose_notify=ssl_shutdownletshutdownsock=ifnot(close_notifysock)thenignore(close_notifysock:bool)letshutdown_connection=shutdownletoutput_stringssls=letlen=String.lengthsinletto_write=refleninletoffset=ref0inwhile!to_write>0doletwritten=write_substringssls!offset!to_writeinifwritten<=0thenfailwith"output_string failed to write";to_write:=!to_write-written;offset:=!offset+writtendoneletoutput_charsslc=lettmp=String.make1cinletwritten=write_substringssltmp01inifwritten<=0thenfailwith"output_char failed to write"letoutput_intssli=lettmp=Bytes.create4inBytes.settmp0(char_of_int(ilsr24));Bytes.settmp1(char_of_int((ilsr16)land0xff));Bytes.settmp2(char_of_int((ilsr8)land0xff));Bytes.settmp3(char_of_int(iland0xff));ifwritessltmp04<>4thenfailwith"output_int error: all the byte were not sent"letinput_stringssl=letbufsize=1024inletbuf=Bytes.createbufsizeinletret=ref""inletr=ref1inwhile!r<>0dor:=readsslbuf0bufsize;ret:=!ret^Bytes.sub_stringbuf0!rdone;!retletinput_charssl=lettmp=Bytes.create1inifreadssltmp01<>1thenraiseEnd_of_fileelseBytes.gettmp0letinput_intssl=leti=ref0inlettmp=Bytes.create4inletread=readssltmp04inifread<4thenfailwith"input_int failed to read 4 bytes";i:=int_of_char(Bytes.gettmp0);i:=(!ilsl8)+int_of_char(Bytes.gettmp1);i:=(!ilsl8)+int_of_char(Bytes.gettmp2);i:=(!ilsl8)+int_of_char(Bytes.gettmp3);!iend(* We apply the functor twice. The releasing functions are imported as
default *)includeMake(Runtime_unlock_base)moduleRuntime_lock=Make(Runtime_lock_base)(** Deprecated functions for compatibility with older version *)letread_into_bigarray_blocking:socket->bigarray->int->int->int=Runtime_lock.read_into_bigarrayletwrite_bigarray_blocking:socket->bigarray->int->int->int=Runtime_lock.write_bigarray