(* SPDX-License-Identifier: LGPL-3.0-only WITH OCaml-LGPL-linking-exception *) let interactive = Unix.(isatty stdin) let err q v = match q, v with | true, _ -> Printf.ifprintf stdout | _ -> Printf.eprintf and out q v = match q, v with | true, _ | _, true -> Printf.ifprintf stdout | _ -> Printf.printf let header q v = out q v "SID \ IDAUTH \ NSUB SUBAUTHS\n%!" let emit q v s = let ss = Sid.to_string s and ia = Sid.get_ident_auth s and sas = Sid.get_sub_auths s in out q v "%-30s %-15s %-4d %s\n" ss (Stdint.Uint64.to_string ia) (Array.length sas) (Array.map Stdint.Uint32.to_string sas |> Array.to_list |> String.concat " ") let handle_input q v s = match String.trim s with | "" -> `Nothing | s -> (match Sid.of_string s with | Ok s -> `Done s | Error e -> err q v "ERROR: %s\n%!" e; `Junk) let from_argv sids = let rest = ref sids in let next () = match !rest with | [] -> `Done | hd::tl -> rest := tl; `Sid hd in next let from_stdin () = try `Sid (input_line stdin) with End_of_file -> `Done let traverse q v next = let rec aux n ne = match next () with | `Done -> if ne = 0 then Ok n else Error (n, ne) | `Sid s -> begin match handle_input q v s with | `Nothing -> aux n ne | `Junk -> aux (n + 1) (ne + 1) | `Done s -> begin if n = 0 then header q v; emit q v s; if interactive then flush_all (); aux (n + 1) ne end end in aux 0 0 let sidparse validate quiet args = let next = match args with | [] -> begin if interactive then err quiet validate "reading input from stdin\n%!"; from_stdin end | sids -> from_argv sids in match traverse quiet validate next with | Ok _ -> 0 | Error (n, ne) -> if not quiet then err quiet validate "processed %d items with %d errors\n" n ne; -1 open Cmdliner let arg_validate = let doc = "Do not print table of constituent parts, only error messages." in Arg.(value & flag & info ["v"; "validate"] ~doc) let arg_quiet = let doc = "Do not print anything at all." in Arg.(value & flag & info ["q"; "quiet"] ~doc) let arg_sid = let doc = "Inputs specified on command line as positional arguments." in Arg.(value & (pos_all string []) & info [] ~docv:"SID" ~doc) let cmd_sidparse = let exits = Term.exit_info ~doc:"errors occurred" (-1) :: Term.default_exits in let doc = "Process Windows security identifiers." in let man = [ `P "This command will parse SIDs (security identifiers) \ from the command line and standard input." ; `P "The input is expected to consist of one SID per argument or \ line, respectively, in “string format” (cf. [MS-DTYP] 2.4.21)." ; `S Manpage.s_see_also ; `P "Consult Microsoft’s documentation for more information. \ SIDs are specified in MS-DTYP \ (https://msdn.microsoft.com/en-us/library/cc230273.aspx) \ with references to the Open Group RPC standard \ (http://www.opengroup.org/dce/download/)." ; `S Manpage.s_examples ; `P "Parse a number of SIDs supplied on command line:" ; `Pre "$ sidparse S-1-0 S-1-1\n\ SID IDAUTH NSUB SUBAUTHS\n\ S-1-0 0 0\n\ S-1-1 1 0\n" ; `P "Parse a number of SIDs from standard input:" ; `Pre "$ sidparse < Term.exit_status ;;