123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137letsrc=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_location="/System/Library/Keychains/SystemRootCertificates.keychain"externaliter_on_anchors:(string->unit)->unit="ca_certs_iter_on_anchors"letget_anchors()=letder_list=ref[]inmatchiter_on_anchors(funder_cert->der_list:=Cstruct.of_stringder_cert::!der_list)with|()->Ok!der_list|exceptionFailuremsg->Error(`Msgmsg)let(let*)=Result.bindletrecmap_mfl=matchlwith|[]->Ok[]|x::xs->let*y=fxinlet*ys=map_mfxsinOk(y::ys)(** 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()inlet*cert_list=map_mX509.Certificate.decode_deranchorsinOk(X509.Certificate.encode_pem_multiplecert_list|>Cstruct.to_string)lettrust_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.info(funm->m"using %s (from SSL_CERT_FILE)"x);detect_onex|_,Somex->Log.info(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"->letcmd=Bos.Cmd.(v"security"%"find-certificate"%"-a"%"-p"%macos_keychain_location)inBos.OS.Cmd.(run_outcmd|>out_string|>success)|s->Error(`Msg("ca-certs: unknown system "^s^".\n"^issue)))letauthenticator?crls?allowed_hashes()=let*data=trust_anchors()inlettime()=Some(Ptime_clock.now())in(* we cannot use decode_pem_multiple since this fails on the first
undecodable certificate - while we'd like to stay operational, and ignore
some certificates *)letd="-----"inletsep=d^"END CERTIFICATE"^dinletcerts=Astring.String.cuts~sep~empty:falsedatainletcas=letaffix=d^"BEGIN CERTIFICATE"^dinList.fold_left(funaccdata->ifnot(Astring.String.is_infix~affixdata)thenaccelseletdata=data^sepinmatchX509.Certificate.decode_pem(Cstruct.of_stringdata)with|Okca->ca::acc|Error(`Msgmsg)->Log.warn(funm->m"Failed to decode a trust anchor %s."msg);Log.debug(funm->m"Full certificate:@.%s"data);acc)[]certsinletcas=List.revcasinmatchcaswith|[]->Error(`Msg("ca-certs: empty trust anchors.\n"^issue))|_->Ok(X509.Authenticator.chain_of_trust?crls?allowed_hashes~timecas)