2018-10-23 22:13:47 +00:00
|
|
|
(* (c) 2018 Hannes Mehnert, all rights reserved *)
|
|
|
|
|
2018-10-25 14:55:54 +00:00
|
|
|
open Rresult
|
2018-10-23 22:10:08 +00:00
|
|
|
open Rresult.R.Infix
|
2019-05-03 18:57:09 +00:00
|
|
|
open X509
|
2017-05-26 14:30:34 +00:00
|
|
|
|
2018-10-28 00:03:27 +00:00
|
|
|
(* we skip all non-albatross certificates *)
|
2018-10-28 19:50:10 +00:00
|
|
|
let cert_name cert =
|
2019-05-03 18:57:09 +00:00
|
|
|
match Extension.(find (Unsupported Vmm_asn.oid) (Certificate.extensions cert)) with
|
2018-10-28 21:29:45 +00:00
|
|
|
| None -> Ok None
|
|
|
|
| Some (_, data) ->
|
2019-10-06 21:38:13 +00:00
|
|
|
match X509.(Distinguished_name.common_name (Certificate.subject cert)) with
|
2019-05-03 18:57:09 +00:00
|
|
|
| Some name -> Ok (Some name)
|
2019-11-11 20:49:51 +00:00
|
|
|
| None -> match Vmm_asn.of_cert_extension data with
|
2018-10-28 21:29:45 +00:00
|
|
|
| Error (`Msg _) -> Error (`Msg "couldn't parse albatross extension")
|
2018-11-11 00:21:12 +00:00
|
|
|
| Ok (_, `Policy_cmd pc) ->
|
|
|
|
begin match pc with
|
|
|
|
| `Policy_add _ -> Error (`Msg "policy add may not have an empty name")
|
|
|
|
| `Policy_remove -> Error (`Msg "policy remove may not have an empty name")
|
|
|
|
| `Policy_info -> Ok None
|
|
|
|
end
|
|
|
|
| Ok (_, `Block_cmd bc) ->
|
|
|
|
begin match bc with
|
|
|
|
| `Block_add _ -> Error (`Msg "block add may not have an empty name")
|
|
|
|
| `Block_remove -> Error (`Msg "block remove may not have an empty name")
|
|
|
|
| `Block_info -> Ok None
|
|
|
|
end
|
2018-10-28 22:06:15 +00:00
|
|
|
| _ -> Ok None
|
2018-10-28 19:50:10 +00:00
|
|
|
|
2018-10-28 00:03:27 +00:00
|
|
|
let name chain =
|
|
|
|
List.fold_left (fun acc cert ->
|
2018-10-28 21:29:45 +00:00
|
|
|
match acc, cert_name cert with
|
|
|
|
| Error e, _ -> Error e
|
|
|
|
| _, Error e -> Error e
|
|
|
|
| Ok acc, Ok None -> Ok acc
|
2019-11-09 01:44:31 +00:00
|
|
|
| Ok acc, Ok (Some data) -> Vmm_core.Name.append data acc)
|
|
|
|
(Ok Vmm_core.Name.root) chain
|
2018-10-25 14:55:54 +00:00
|
|
|
|
|
|
|
(* this separates the leaf and top-level certificate from the chain,
|
|
|
|
and also reverses the intermediates (to be (leaf, CA -> subCA -> subCA')
|
|
|
|
in which subCA' signed leaf *)
|
|
|
|
let separate_chain = function
|
|
|
|
| [] -> Error (`Msg "empty chain")
|
|
|
|
| [ leaf ] -> Ok (leaf, [])
|
|
|
|
| leaf :: xs -> Ok (leaf, List.rev xs)
|
|
|
|
|
2019-11-11 20:49:51 +00:00
|
|
|
let wire_command_of_cert cert =
|
2019-05-03 18:57:09 +00:00
|
|
|
match Extension.(find (Unsupported Vmm_asn.oid) (Certificate.extensions cert)) with
|
2018-10-28 19:50:10 +00:00
|
|
|
| None -> Error `Not_present
|
2018-10-25 14:55:54 +00:00
|
|
|
| Some (_, data) ->
|
2019-11-11 20:49:51 +00:00
|
|
|
Vmm_asn.of_cert_extension data >>= fun (v, wire) ->
|
|
|
|
if not Vmm_commands.(is_current v) then
|
|
|
|
Logs.warn (fun m -> m "version mismatch, received %a current %a"
|
|
|
|
Vmm_commands.pp_version v
|
|
|
|
Vmm_commands.pp_version Vmm_commands.current);
|
|
|
|
Ok (v, wire)
|
2017-05-26 14:30:34 +00:00
|
|
|
|
2019-11-11 20:49:51 +00:00
|
|
|
let extract_policies chain =
|
2018-10-28 19:50:10 +00:00
|
|
|
List.fold_left (fun acc cert ->
|
2019-11-11 20:49:51 +00:00
|
|
|
match acc, wire_command_of_cert cert with
|
2018-10-28 19:50:10 +00:00
|
|
|
| Error e, _ -> Error e
|
|
|
|
| Ok acc, Error `Not_present -> Ok acc
|
2019-05-03 18:57:09 +00:00
|
|
|
| Ok _, Error (`Msg msg) -> Error (`Msg msg)
|
2019-11-11 20:49:51 +00:00
|
|
|
| Ok (prefix, acc), Ok (_, `Policy_cmd `Policy_add p) ->
|
2018-11-11 00:21:12 +00:00
|
|
|
(cert_name cert >>= function
|
|
|
|
| None -> Ok prefix
|
|
|
|
| Some x -> Vmm_core.Name.prepend x prefix) >>| fun name ->
|
2018-10-28 21:29:45 +00:00
|
|
|
(name, (name, p) :: acc)
|
2018-10-28 19:50:10 +00:00
|
|
|
| _, Ok wire ->
|
2019-11-11 20:49:51 +00:00
|
|
|
R.error_msgf "unexpected wire %a" Vmm_commands.pp (snd wire))
|
2018-11-11 00:21:12 +00:00
|
|
|
(Ok (Vmm_core.Name.root, [])) chain
|
2018-10-28 19:50:10 +00:00
|
|
|
|
2019-11-11 20:49:51 +00:00
|
|
|
let handle chain =
|
2019-11-09 01:44:31 +00:00
|
|
|
(if List.length chain < 10 then
|
|
|
|
Ok ()
|
|
|
|
else
|
|
|
|
Error (`Msg "certificate chain too long")) >>= fun () ->
|
2018-10-28 00:03:27 +00:00
|
|
|
separate_chain chain >>= fun (leaf, rest) ->
|
2019-11-09 01:44:31 +00:00
|
|
|
(* use subject common names of intermediate certs as prefix *)
|
|
|
|
name rest >>= fun name' ->
|
|
|
|
(* and subject common name of leaf certificate -- allowing dots in CN -- as postfix *)
|
|
|
|
(cert_name leaf >>= function
|
2019-11-11 21:30:53 +00:00
|
|
|
| None | Some "." -> Ok name'
|
2019-11-09 01:44:31 +00:00
|
|
|
| Some x ->
|
|
|
|
Vmm_core.Name.of_string x >>| fun post ->
|
|
|
|
Vmm_core.Name.concat name' post) >>= fun name ->
|
|
|
|
Logs.debug (fun m -> m "name is %a leaf is %a, chain %a"
|
|
|
|
Vmm_core.Name.pp name Certificate.pp leaf
|
2019-05-03 18:57:09 +00:00
|
|
|
Fmt.(list ~sep:(unit " -> ") Certificate.pp) rest);
|
2019-11-11 20:49:51 +00:00
|
|
|
extract_policies rest >>= fun (_, policies) ->
|
2018-10-23 22:10:08 +00:00
|
|
|
(* TODO: logging let login_hdr, login_ev = Log.hdr name, `Login addr in *)
|
2019-11-11 20:49:51 +00:00
|
|
|
match wire_command_of_cert leaf with
|
|
|
|
| Error `Msg p -> Error (`Msg p)
|
|
|
|
| Error `Not_present ->
|
2018-10-28 19:50:10 +00:00
|
|
|
Error (`Msg "leaf certificate does not contain an albatross extension")
|
2019-11-11 20:49:51 +00:00
|
|
|
| Ok (v, wire) ->
|
2018-10-28 19:50:10 +00:00
|
|
|
(* we only allow some commands via certificate *)
|
|
|
|
match wire with
|
|
|
|
| `Console_cmd (`Console_subscribe _)
|
|
|
|
| `Stats_cmd `Stats_subscribe
|
|
|
|
| `Log_cmd (`Log_subscribe _)
|
2018-11-13 00:02:05 +00:00
|
|
|
| `Unikernel_cmd _
|
2019-11-11 20:49:51 +00:00
|
|
|
| `Policy_cmd `Policy_info -> Ok (name, policies, v, wire)
|
2018-10-28 19:50:10 +00:00
|
|
|
| _ -> Error (`Msg "unexpected command")
|