123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180letsrc=Logs.Src.create"ca-certs"~doc:"CA certificates"moduleLog=(valLogs.src_logsrc:Logs.LOG)letissue={|Please report an issue at https://github.com/mirage/ca-certs, including:
- the output of uname -s
- the distribution you use
- the location of default trust anchors (if known)
|}letdetect_onepath=letpath'=Fpath.vpathinmatchBos.OS.Path.existspath'with|Oktrue->Bos.OS.File.readpath'|_->Error(`Msg("ca-certs: no trust anchor file found, looked into "^path^".\n"^issue))letdetect_listpaths=letrecone=function|[]->Error(`Msg("ca-certs: no trust anchor file found, looked into "^String.concat", "paths^".\n"^issue))|path::paths->(matchdetect_onepathwithOkdata->Okdata|Error_->onepaths)inonepaths(* from https://golang.org/src/crypto/x509/root_linux.go *)letlinux_locations=[(* Debian/Ubuntu/Gentoo etc. *)"/etc/ssl/certs/ca-certificates.crt";(* CentOS/RHEL 7 *)"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem";(* OpenSUSE *)"/etc/ssl/ca-bundle.pem";](* from https://golang.org/src/crypto/x509/root_bsd.go *)letopenbsd_location="/etc/ssl/cert.pem"letfreebsd_location="/usr/local/share/certs/ca-root-nss.crt"letmacos_keychain_locations=["/System/Library/Keychains/SystemRootCertificates.keychain";"/Library/Keychains/System.keychain";]externaliter_on_anchors:(string->unit)->unit="ca_certs_iter_on_anchors"letget_anchors()=letder_list=ref[]inmatchiter_on_anchors(funder_cert->der_list:=der_cert::!der_list)with|()->Ok!der_list|exceptionFailuremsg->Error(`Msgmsg)let(let*)=Result.bind(** Load certificates from Windows' ["ROOT"] system certificate store. The C API
returns a list of DER-encoded certificates. These are decoded and reencoded
as a single PEM certificate. *)letwindows_trust_anchors()=let*anchors=get_anchors()inletcert_list,err_count=List.fold_left(fun(acc,err_count)cert->matchX509.Certificate.decode_dercertwith|Okcert->(cert::acc,err_count)|Error(`Msgmsg)->Log.debug(funm->m"Ignoring undecodable trust anchor: %s."msg);Log.debug(funm->m"Full certificate:@.%a"(Ohex.pp_hexdump())cert);(acc,err_count+1))([],0)anchorsiniferr_count>0thenLog.warn(funm->m"Ignored %u trust anchors."err_count);Ok(X509.Certificate.encode_pem_multiplecert_list)letsystem_trust_anchors()=ifSys.win32thenwindows_trust_anchors()else(* NixOS is special and sets "NIX_SSL_CERT_FILE" as location during builds *)match(Sys.getenv_opt"SSL_CERT_FILE",Sys.getenv_opt"NIX_SSL_CERT_FILE")with|Somex,_->Log.debug(funm->m"using %s (from SSL_CERT_FILE)"x);detect_onex|_,Somex->Log.debug(funm->m"using %s (from NIX_SSL_CERT_FILE)"x);detect_onex|None,None->(letcmd=Bos.Cmd.(v"uname"%"-s")inlet*os=Bos.OS.Cmd.(run_outcmd|>out_string|>success)inmatchoswith|"FreeBSD"->detect_onefreebsd_location|"OpenBSD"->detect_oneopenbsd_location|"Linux"->detect_listlinux_locations|"Darwin"->macos_keychain_locations|>List.map(funpath->letcmd=Bos.Cmd.(v"security"%"find-certificate"%"-a"%"-p"%path)inBos.OS.Cmd.(run_outcmd|>out_string|>success))|>List.fold_left(funacccert->match(cert,acc)with|Okcert,Okacc->Ok(cert^"\n"^acc)|Okcert,Error(`Msgmsg)->Log.warn(funm->m"ignoring error %s (got another set of \
certificates)"msg);Okcert|Errore,Ok""->Errore|Error(`Msgmsg),Okx->Log.warn(funm->m"ignoring error %s (already have another set of \
certificates)"msg);Okx|Errore,Error(`Msgmsg)->Log.warn(funm->m"ignoring error %s (got another error)"msg);Errore)(Ok"")|>Result.map_error(function`Msgmsg->`Msg("ca-certs: no trust anchor file found on macOS: "^msg^".\n"^issue))|s->Error(`Msg("ca-certs: unknown system "^s^".\n"^issue)))letextra_trust_anchors()=matchSys.getenv_opt"OCAML_EXTRA_CA_CERTS"with|None->Ok""|Somex->Log.info(funm->m"using %s (from OCAML_EXTRA_CA_CERTS)"x);detect_onexlettrust_anchors()=let*cas=system_trust_anchors()inmatchextra_trust_anchors()with|Ok""->Okcas|Okextra_cas->Ok(cas^"\n"^extra_cas)|Error(`Msgmsg)->Log.warn(funm->m"Ignoring extra trust anchors: %s."msg);Okcasletdecode_pem_multipledata=lettas,err_count=X509.Certificate.fold_decode_pem_multiple(fun(acc,err_count)->function|Okt->(t::acc,err_count)|Error(`Msgmsg)->Log.debug(funm->m"Ignoring undecodable trust anchor: %s."msg);(acc,err_count+1))([],0)datainiferr_count>0thenLog.warn(funm->m"Ignored %u trust anchors."err_count);tasletauthenticator?crls?allowed_hashes()=let*data=trust_anchors()inmatchdecode_pem_multipledatawith|[]->Error(`Msg("ca-certs: empty trust anchors.\n"^issue))|cas->lettime()=Some(Ptime_clock.now())inOk(X509.Authenticator.chain_of_trust?crls?allowed_hashes~timecas)