123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122letsrc=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->Rresult.R.error_msgmsgletrecmap_mfl=matchlwith|[]->Ok[]|x::xs->letopenRresult.Rinfx>>=funy->map_mfxs>>|funys->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()=letopenRresult.Ringet_anchors()>>=map_mX509.Certificate.decode_der>>|funcert_list->X509.Certificate.encode_pem_multiplecert_list|>Cstruct.to_stringlettrust_anchors()=letopenRresult.R.InfixinifSys.win32thenwindows_trust_anchors()elseletcmd=Bos.Cmd.(v"uname"%"-s")inBos.OS.Cmd.(run_outcmd|>out_string|>success)>>=function|"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?hash_whitelist()=letopenRresult.R.Infixintrust_anchors()>>=fundata->lettime()=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 *)letsep="-----END CERTIFICATE-----"inletcerts=Astring.String.cuts~sep~empty:falsedatainletcas=List.fold_left(funaccdata->letdata=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?hash_whitelist~timecas)