adapt to X509 0.7.0 API, minor comment and doc tweaks

This commit is contained in:
Hannes Mehnert 2019-05-03 20:57:09 +02:00
parent 92c325a7f9
commit 50ed6a8d1e
7 changed files with 133 additions and 118 deletions

View File

@ -19,7 +19,7 @@ depends: [
"cmdliner" {>= "1.0.0"} "cmdliner" {>= "1.0.0"}
"fmt" "fmt"
"astring" "astring"
"x509" {>= "0.6.0"} "x509" {>= "0.7.0"}
"tls" {>= "0.9.0"} "tls" {>= "0.9.0"}
"nocrypto" "nocrypto"
"asn1-combinators" {>= "0.2.0"} "asn1-combinators" {>= "0.2.0"}

View File

@ -1,6 +1,7 @@
(* (c) 2018 Hannes Mehnert, all rights reserved *) (* (c) 2018 Hannes Mehnert, all rights reserved *)
open Lwt.Infix open Lwt.Infix
open X509
let version = `AV3 let version = `AV3
@ -16,13 +17,16 @@ let read fd =
in in
loop () loop ()
let key_ids pub issuer = let key_ids exts pub issuer =
let auth = (Some (X509.key_id issuer), [], None) in let auth = (Some (Public_key.id issuer), General_name.empty, None) in
[ (false, `Subject_key_id (X509.key_id pub)) ; (false, `Authority_key_id auth) ] Extension.(add Subject_key_id (false, (Public_key.id pub))
(add Authority_key_id (false, auth) exts))
let timestamps validity = let timestamps validity =
let now = Ptime_clock.now () in let now = Ptime_clock.now () in
match match
(* subtracting some seconds here to not require perfectly synchronised
clocks on client and server *)
Ptime.sub_span now (Ptime.Span.of_int_s 10), Ptime.sub_span now (Ptime.Span.of_int_s 10),
Ptime.add_span now (Ptime.Span.of_int_s validity) Ptime.add_span now (Ptime.Span.of_int_s validity)
with with
@ -31,36 +35,46 @@ let timestamps validity =
let handle (host, port) cert key ca id (cmd : Vmm_commands.t) = let handle (host, port) cert key ca id (cmd : Vmm_commands.t) =
Vmm_lwt.read_from_file cert >>= fun cert_cs -> Vmm_lwt.read_from_file cert >>= fun cert_cs ->
let cert = X509.Encoding.Pem.Certificate.of_pem_cstruct1 cert_cs in
Vmm_lwt.read_from_file key >>= fun key_cs -> Vmm_lwt.read_from_file key >>= fun key_cs ->
let key = X509.Encoding.Pem.Private_key.of_pem_cstruct1 key_cs in match Certificate.decode_pem cert_cs, Private_key.decode_pem key_cs with
| Error (`Msg e), _ ->
Lwt.fail_with ("couldn't parse certificate (" ^ cert ^ "): " ^ e)
| _, Error (`Msg e) ->
Lwt.fail_with ("couldn't parse private key (" ^ key ^ "): " ^ e)
| Ok cert, Ok key ->
let tmpkey = Nocrypto.Rsa.generate 4096 in let tmpkey = Nocrypto.Rsa.generate 4096 in
let name = Vmm_core.Name.to_string id in let name = Vmm_core.Name.to_string id in
let extensions = let extensions =
[ (true, `Key_usage [ `Digital_signature ; `Key_encipherment ]) let v = Vmm_asn.cert_extension_to_cstruct (version, cmd) in
; (true, `Basic_constraints (false, None)) Extension.(add Key_usage (true, [ `Digital_signature ; `Key_encipherment ])
; (true, `Ext_key_usage [`Client_auth]) ; (add Basic_constraints (true, (false, None))
(false, `Unsupported (Vmm_asn.oid, Vmm_asn.cert_extension_to_cstruct (version, cmd))) ] in (add Ext_key_usage (true, [ `Client_auth ])
(singleton (Unsupported Vmm_asn.oid) (false, v)))))
in
let csr = let csr =
let name = [ `CN name ] in let name = Distinguished_name.(singleton CN name) in
X509.CA.request name ~extensions:[`Extensions extensions] (`RSA tmpkey) let extensions = Signing_request.Ext.(singleton Extensions extensions) in
Signing_request.create name ~extensions (`RSA tmpkey)
in in
let mycert = let mycert =
let valid_from, valid_until = timestamps 300 in let valid_from, valid_until = timestamps 300 in
let extensions = let extensions =
let capub = match key with `RSA key -> Nocrypto.Rsa.pub_of_priv key in let capub = match key with `RSA key -> Nocrypto.Rsa.pub_of_priv key in
extensions @ key_ids (X509.CA.info csr).X509.CA.public_key (`RSA capub) key_ids extensions Signing_request.((info csr).public_key) (`RSA capub)
in in
let issuer = X509.subject cert in let issuer = Certificate.subject cert in
X509.CA.sign csr ~valid_from ~valid_until ~extensions key issuer Signing_request.sign csr ~valid_from ~valid_until ~extensions key issuer
in in
let certificates = `Single ([ mycert ; cert ], tmpkey) in let certificates = `Single ([ mycert ; cert ], tmpkey) in
X509_lwt.authenticator (`Ca_file ca) >>= fun authenticator -> X509_lwt.authenticator (`Ca_file ca) >>= fun authenticator ->
Lwt_unix.gethostbyname host >>= fun host_entry -> Lwt_unix.gethostbyname host >>= fun host_entry ->
let host_inet_addr = Array.get host_entry.Lwt_unix.h_addr_list 0 in let host_inet_addr = Array.get host_entry.Lwt_unix.h_addr_list 0 in
let fd = Lwt_unix.socket host_entry.Lwt_unix.h_addrtype Lwt_unix.SOCK_STREAM 0 in let fd = Lwt_unix.(socket host_entry.h_addrtype SOCK_STREAM 0) in
Logs.debug (fun m -> m "connecting to remote host") ; Logs.debug (fun m -> m "connecting to remote host") ;
Lwt_unix.connect fd (Lwt_unix.ADDR_INET (host_inet_addr, port)) >>= fun () -> Lwt_unix.connect fd (Lwt_unix.ADDR_INET (host_inet_addr, port)) >>= fun () ->
(* reneg true to allow re-negotiation over the server-authenticated TLS
channel (to transport client certificate encrypted), once TLS 1.3 is in
(and required) be removed! *)
let client = Tls.Config.client ~reneg:true ~certificates ~authenticator () in let client = Tls.Config.client ~reneg:true ~certificates ~authenticator () in
Tls_lwt.Unix.client_of_fd client (* TODO ~host *) fd >>= fun t -> Tls_lwt.Unix.client_of_fd client (* TODO ~host *) fd >>= fun t ->
Logs.debug (fun m -> m "finished tls handshake") ; Logs.debug (fun m -> m "finished tls handshake") ;

View File

@ -29,19 +29,19 @@ let append name data =
go 0 >>= fun () -> go 0 >>= fun () ->
safe Unix.close fd safe Unix.close fd
let key_ids pub issuer = let key_ids exts pub issuer =
let auth = (Some (X509.key_id issuer), [], None) in let auth = Some (X509.Public_key.id issuer), X509.General_name.empty, None in
[ (false, `Subject_key_id (X509.key_id pub)) ; (false, `Authority_key_id auth) ] X509.Extension.(add Subject_key_id (false, X509.Public_key.id pub)
(add Authority_key_id (false, auth) exts))
let sign ?dbname ?certname extensions issuer key csr delta = let sign ?dbname ?certname extensions issuer key csr delta =
let open Rresult.R.Infix in let open Rresult.R.Infix in
(match certname with (match certname with
| Some x -> Ok x | Some x -> Ok x
| None -> | None ->
(try Ok (List.find (function `CN _ -> true | _ -> false) (X509.CA.info csr).X509.CA.subject) match X509.(Distinguished_name.find CN Signing_request.((info csr).subject)) with
with Not_found -> Error (`Msg "unable to discover certificate name")) >>= function | Some name -> Ok name
| `CN name -> Ok name | None -> Error (`Msg "couldn't find name (no common name in CSR subject)")) >>= fun certname ->
| _ -> Error (`Msg "cannot happen")) >>= fun certname ->
timestamps delta >>= fun (valid_from, valid_until) -> timestamps delta >>= fun (valid_from, valid_until) ->
let extensions = let extensions =
match dbname with match dbname with
@ -50,14 +50,14 @@ let sign ?dbname ?certname extensions issuer key csr delta =
match key with match key with
| `RSA priv -> | `RSA priv ->
let capub = `RSA (Nocrypto.Rsa.pub_of_priv priv) in let capub = `RSA (Nocrypto.Rsa.pub_of_priv priv) in
extensions @ key_ids (X509.CA.info csr).X509.CA.public_key capub key_ids extensions X509.Signing_request.((info csr).public_key) capub
in in
let cert = X509.CA.sign csr ~valid_from ~valid_until ~extensions key issuer in let cert = X509.Signing_request.sign csr ~valid_from ~valid_until ~extensions key issuer in
(match dbname with (match dbname with
| None -> Ok () (* no DB! *) | None -> Ok () (* no DB! *)
| Some dbname -> | Some dbname ->
append dbname (Printf.sprintf "%s %s\n" (Z.to_string (X509.serial cert)) certname)) >>= fun () -> append dbname (Printf.sprintf "%s %s\n" (Z.to_string (X509.Certificate.serial cert)) certname)) >>= fun () ->
let enc = X509.Encoding.Pem.Certificate.to_pem_cstruct1 cert in let enc = X509.Certificate.encode_pem cert in
Bos.OS.File.write Fpath.(v certname + "pem") (Cstruct.to_string enc) Bos.OS.File.write Fpath.(v certname + "pem") (Cstruct.to_string enc)
let priv_key ?(bits = 2048) fn name = let priv_key ?(bits = 2048) fn name =
@ -70,11 +70,11 @@ let priv_key ?(bits = 2048) fn name =
| false -> | false ->
Logs.info (fun m -> m "creating new RSA key %a" Fpath.pp file) ; Logs.info (fun m -> m "creating new RSA key %a" Fpath.pp file) ;
let priv = `RSA (Nocrypto.Rsa.generate bits) in let priv = `RSA (Nocrypto.Rsa.generate bits) in
Bos.OS.File.write ~mode:0o400 file (Cstruct.to_string (X509.Encoding.Pem.Private_key.to_pem_cstruct1 priv)) >>= fun () -> Bos.OS.File.write ~mode:0o400 file (Cstruct.to_string (X509.Private_key.encode_pem priv)) >>= fun () ->
Ok priv Ok priv
| true -> | true ->
Bos.OS.File.read file >>= fun s -> Bos.OS.File.read file >>= fun s ->
Ok (X509.Encoding.Pem.Private_key.of_pem_cstruct1 (Cstruct.of_string s)) X509.Private_key.decode_pem (Cstruct.of_string s)
open Cmdliner open Cmdliner

View File

@ -1,47 +1,45 @@
(* (c) 2017, 2018 Hannes Mehnert, all rights reserved *) (* (c) 2017, 2018 Hannes Mehnert, all rights reserved *)
open Rresult.R.Infix open Rresult.R.Infix
open X509
open Albatross_provision
let l_exts = let l_exts =
[ (true, `Key_usage [ `Digital_signature ; `Key_encipherment ]) Extension.(add Key_usage (true, [ `Digital_signature ; `Key_encipherment ])
; (true, `Basic_constraints (false, None)) (add Basic_constraints (true, (false, None))
; (true, `Ext_key_usage [`Client_auth]) ] (singleton Ext_key_usage (true, [ `Client_auth ]))))
let d_exts ?len () = let d_exts ?len () =
[ (true, (`Basic_constraints (true, len))) let kus =
; (true, (`Key_usage [ `Key_cert_sign ; `CRL_sign ; `Digital_signature ; `Content_commitment ])) ] [ `Key_cert_sign ; `CRL_sign ; `Digital_signature ; `Content_commitment ]
in
Extension.(add Basic_constraints (true, (true, len))
(singleton Key_usage (true, kus)))
let s_exts = let s_exts =
[ (true, `Key_usage [ `Digital_signature ; `Key_encipherment ]) Extension.(add Key_usage (true, [ `Digital_signature ; `Key_encipherment ])
; (true, `Basic_constraints (false, None)) (add Basic_constraints (true, (false, None))
; (true, `Ext_key_usage [`Server_auth]) ] (singleton Ext_key_usage (true, [ `Server_auth ]))))
let albatross_extension csr = let albatross_extension csr =
let req_exts = let req_exts =
match match Signing_request.(Ext.(find Extensions ((info csr).extensions))) with
List.find (function `Extensions _ -> true | _ -> false) X509.CA.((info csr).extensions) | Some x -> x
with | None -> Extension.empty
| exception Not_found -> []
| `Extensions x -> x
| _ -> []
in in
match match Extension.(find (Unsupported Vmm_asn.oid) req_exts) with
List.filter (function | Some (_, v) -> Ok v
| (_, `Unsupported (oid, _)) when Asn.OID.equal oid Vmm_asn.oid -> true | None -> Error (`Msg "couldn't find albatross extension in CSR")
| _ -> false)
req_exts
with
| [ (_, `Unsupported (_, v)) as ext ] -> Ok (ext, v)
| _ -> Error (`Msg "couldn't find albatross extension in CSR")
let sign_csr dbname cacert key csr days = let sign_csr dbname cacert key csr days =
let ri = X509.CA.info csr in let ri = Signing_request.info csr in
Logs.app (fun m -> m "signing certificate with subject %s" Logs.app (fun m -> m "signing certificate with subject %a"
(X509.distinguished_name_to_string ri.X509.CA.subject)) ; Distinguished_name.pp ri.Signing_request.subject);
let issuer = X509.subject cacert in let issuer = Certificate.subject cacert in
(* TODO: check delegation! verify whitelisted commands!? *) (* TODO: check delegation! verify whitelisted commands!? *)
match albatross_extension csr with match albatross_extension csr with
| Ok (ext, v) -> | Ok v ->
Vmm_asn.cert_extension_of_cstruct v >>= fun (version, cmd) -> Vmm_asn.cert_extension_of_cstruct v >>= fun (version, cmd) ->
(if Vmm_commands.version_eq version version then (if Vmm_commands.version_eq version version then
Ok () Ok ()
@ -51,19 +49,22 @@ let sign_csr dbname cacert key csr days =
| `Policy_cmd (`Policy_add _) -> d_exts () | `Policy_cmd (`Policy_add _) -> d_exts ()
| _ -> l_exts | _ -> l_exts
in in
Logs.app (fun m -> m "signing %a" Vmm_commands.pp cmd) ; Logs.app (fun m -> m "signing %a" Vmm_commands.pp cmd);
Ok (ext :: exts) >>= fun extensions -> (* the "false" is here since X509 validation bails on exts marked as
Albatross_provision.sign ~dbname extensions issuer key csr (Duration.of_day days) critical (as required), but has no way to supply which extensions
are actually handled by the application / caller *)
let extensions = Extension.(add (Unsupported Vmm_asn.oid) (false, v) exts) in
sign ~dbname extensions issuer key csr (Duration.of_day days)
| Error e -> Error e | Error e -> Error e
let sign _ db cacert cakey csrname days = let sign_main _ db cacert cakey csrname days =
Nocrypto_entropy_unix.initialize () ; Nocrypto_entropy_unix.initialize () ;
Bos.OS.File.read (Fpath.v cacert) >>= fun cacert -> Bos.OS.File.read (Fpath.v cacert) >>= fun cacert ->
let cacert = X509.Encoding.Pem.Certificate.of_pem_cstruct1 (Cstruct.of_string cacert) in Certificate.decode_pem (Cstruct.of_string cacert) >>= fun cacert ->
Bos.OS.File.read (Fpath.v cakey) >>= fun pk -> Bos.OS.File.read (Fpath.v cakey) >>= fun pk ->
let cakey = X509.Encoding.Pem.Private_key.of_pem_cstruct1 (Cstruct.of_string pk) in Private_key.decode_pem (Cstruct.of_string pk) >>= fun cakey ->
Bos.OS.File.read (Fpath.v csrname) >>= fun enc -> Bos.OS.File.read (Fpath.v csrname) >>= fun enc ->
let csr = X509.Encoding.Pem.Certificate_signing_request.of_pem_cstruct1 (Cstruct.of_string enc) in Signing_request.decode_pem (Cstruct.of_string enc) >>= fun csr ->
sign_csr (Fpath.v db) cacert cakey csr days sign_csr (Fpath.v db) cacert cakey csr days
let help _ man_format cmds = function let help _ man_format cmds = function
@ -73,14 +74,14 @@ let help _ man_format cmds = function
let generate _ name db days sname sdays = let generate _ name db days sname sdays =
Nocrypto_entropy_unix.initialize () ; Nocrypto_entropy_unix.initialize () ;
Albatross_provision.priv_key ~bits:4096 None name >>= fun key -> priv_key ~bits:4096 None name >>= fun key ->
let name = [ `CN name ] in let name = Distinguished_name.(singleton CN name) in
let csr = X509.CA.request name key in let csr = Signing_request.create name key in
Albatross_provision.sign ~certname:"cacert" (d_exts ()) name key csr (Duration.of_day days) >>= fun () -> sign ~certname:"cacert" (d_exts ()) name key csr (Duration.of_day days) >>= fun () ->
Albatross_provision.priv_key None sname >>= fun skey -> priv_key None sname >>= fun skey ->
let sname = [ `CN sname ] in let sname = Distinguished_name.(singleton CN sname) in
let csr = X509.CA.request sname skey in let csr = Signing_request.create sname skey in
Albatross_provision.sign ~dbname:(Fpath.v db) s_exts name key csr (Duration.of_day sdays) sign ~dbname:(Fpath.v db) s_exts name key csr (Duration.of_day sdays)
open Cmdliner open Cmdliner
open Albatross_cli open Albatross_cli
@ -115,7 +116,7 @@ let generate_cmd =
[`S "DESCRIPTION"; [`S "DESCRIPTION";
`P "Generates a certificate authority."] `P "Generates a certificate authority."]
in in
Term.(term_result (const generate $ setup_log $ Albatross_provision.nam $ db $ days $ sname $ sday)), Term.(term_result (const generate $ setup_log $ nam $ db $ days $ sname $ sday)),
Term.info "generate" ~doc ~man Term.info "generate" ~doc ~man
let days = let days =
@ -132,7 +133,7 @@ let sign_cmd =
[`S "DESCRIPTION"; [`S "DESCRIPTION";
`P "Signs the certificate signing request."] `P "Signs the certificate signing request."]
in in
Term.(term_result (const sign $ setup_log $ db $ cacert $ key $ csr $ days)), Term.(term_result (const sign_main $ setup_log $ db $ cacert $ key $ csr $ days)),
Term.info "sign" ~doc ~man Term.info "sign" ~doc ~man
let help_cmd = let help_cmd =

View File

@ -8,17 +8,20 @@ open Rresult.R.Infix
let version = `AV3 let version = `AV3
let csr priv name cmd = let csr priv name cmd =
let exts = [ (false, `Unsupported (oid, cert_extension_to_cstruct (version, cmd))) ] let ext =
and name = [ `CN name ] let v = cert_extension_to_cstruct (version, cmd) in
X509.Extension.(singleton (Unsupported oid) (false, v))
and name = X509.Distinguished_name.(singleton CN name)
in in
X509.CA.request name ~extensions:[`Extensions exts] priv let extensions = X509.Signing_request.Ext.(singleton Extensions ext) in
X509.Signing_request.create name ~extensions priv
let jump id cmd = let jump id cmd =
Nocrypto_entropy_unix.initialize () ; Nocrypto_entropy_unix.initialize () ;
let name = Vmm_core.Name.to_string id in let name = Vmm_core.Name.to_string id in
priv_key None name >>= fun priv -> priv_key None name >>= fun priv ->
let csr = csr priv name cmd in let csr = csr priv name cmd in
let enc = X509.Encoding.Pem.Certificate_signing_request.to_pem_cstruct1 csr in let enc = X509.Signing_request.encode_pem csr in
Bos.OS.File.write Fpath.(v name + ".req") (Cstruct.to_string enc) Bos.OS.File.write Fpath.(v name + ".req") (Cstruct.to_string enc)
let info_policy _ name = let info_policy _ name =

View File

@ -2,15 +2,16 @@
open Rresult open Rresult
open Rresult.R.Infix open Rresult.R.Infix
open X509
(* we skip all non-albatross certificates *) (* we skip all non-albatross certificates *)
let cert_name cert = let cert_name cert =
match X509.Extension.unsupported cert Vmm_asn.oid with match Extension.(find (Unsupported Vmm_asn.oid) (Certificate.extensions cert)) with
| None -> Ok None | None -> Ok None
| Some (_, data) -> | Some (_, data) ->
let name = X509.common_name_to_string cert in match Distinguished_name.(find CN (Certificate.subject cert)) with
if name = "" then | Some name -> Ok (Some name)
match Vmm_asn.cert_extension_of_cstruct data with | None -> match Vmm_asn.cert_extension_of_cstruct data with
| Error (`Msg _) -> Error (`Msg "couldn't parse albatross extension") | Error (`Msg _) -> Error (`Msg "couldn't parse albatross extension")
| Ok (_, `Policy_cmd pc) -> | Ok (_, `Policy_cmd pc) ->
begin match pc with begin match pc with
@ -25,7 +26,6 @@ let cert_name cert =
| `Block_info -> Ok None | `Block_info -> Ok None
end end
| _ -> Ok None | _ -> Ok None
else Ok (Some name)
let name chain = let name chain =
List.fold_left (fun acc cert -> List.fold_left (fun acc cert ->
@ -49,12 +49,10 @@ let separate_chain = function
| leaf :: xs -> Ok (leaf, List.rev xs) | leaf :: xs -> Ok (leaf, List.rev xs)
let wire_command_of_cert version cert = let wire_command_of_cert version cert =
match X509.Extension.unsupported cert Vmm_asn.oid with match Extension.(find (Unsupported Vmm_asn.oid) (Certificate.extensions cert)) with
| None -> Error `Not_present | None -> Error `Not_present
| Some (_, data) -> | Some (_, data) ->
match Vmm_asn.cert_extension_of_cstruct data with Vmm_asn.cert_extension_of_cstruct data >>= fun (v, wire) ->
| Error (`Msg p) -> Error (`Parse p)
| Ok (v, wire) ->
if not (Vmm_commands.version_eq v version) then if not (Vmm_commands.version_eq v version) then
Error (`Version v) Error (`Version v)
else else
@ -65,7 +63,7 @@ let extract_policies version chain =
match acc, wire_command_of_cert version cert with match acc, wire_command_of_cert version cert with
| Error e, _ -> Error e | Error e, _ -> Error e
| Ok acc, Error `Not_present -> Ok acc | Ok acc, Error `Not_present -> Ok acc
| Ok _, Error (`Parse msg) -> Error (`Msg msg) | Ok _, Error (`Msg msg) -> Error (`Msg msg)
| Ok _, Error (`Version received) -> | Ok _, Error (`Version received) ->
R.error_msgf "unexpected version %a (expected %a)" R.error_msgf "unexpected version %a (expected %a)"
Vmm_commands.pp_version received Vmm_commands.pp_version received
@ -82,14 +80,13 @@ let extract_policies version chain =
let handle version chain = let handle version chain =
separate_chain chain >>= fun (leaf, rest) -> separate_chain chain >>= fun (leaf, rest) ->
name chain >>= fun name -> name chain >>= fun name ->
Logs.debug (fun m -> m "leaf is %s, chain %a" Logs.debug (fun m -> m "leaf is %a, chain %a"
(X509.common_name_to_string leaf) Certificate.pp leaf
Fmt.(list ~sep:(unit " -> ") string) Fmt.(list ~sep:(unit " -> ") Certificate.pp) rest);
(List.map X509.common_name_to_string rest)) ;
extract_policies version rest >>= fun (_, policies) -> extract_policies version rest >>= fun (_, policies) ->
(* TODO: logging let login_hdr, login_ev = Log.hdr name, `Login addr in *) (* TODO: logging let login_hdr, login_ev = Log.hdr name, `Login addr in *)
match wire_command_of_cert version leaf with match wire_command_of_cert version leaf with
| Error (`Parse p) -> Error (`Msg p) | Error (`Msg p) -> Error (`Msg p)
| Error (`Not_present) -> | Error (`Not_present) ->
Error (`Msg "leaf certificate does not contain an albatross extension") Error (`Msg "leaf certificate does not contain an albatross extension")
| Error (`Version received) -> | Error (`Version received) ->

View File

@ -1,10 +1,10 @@
(* (c) 2018 Hannes Mehnert, all rights reserved *) (* (c) 2018 Hannes Mehnert, all rights reserved *)
val wire_command_of_cert : Vmm_commands.version -> X509.t -> val wire_command_of_cert : Vmm_commands.version -> X509.Certificate.t ->
(Vmm_commands.t, [> `Parse of string | `Not_present | `Version of Vmm_commands.version ]) result (Vmm_commands.t, [> `Msg of string | `Not_present | `Version of Vmm_commands.version ]) result
val handle : val handle :
Vmm_commands.version -> Vmm_commands.version ->
X509.t list -> X509.Certificate.t list ->
(Vmm_core.Name.t * (Vmm_core.Name.t * Vmm_core.Policy.t) list * Vmm_commands.t, (Vmm_core.Name.t * (Vmm_core.Name.t * Vmm_core.Policy.t) list * Vmm_commands.t,
[> `Msg of string ]) Result.result [> `Msg of string ]) Result.result