summaryrefslogtreecommitdiff
path: root/util/sidparse.ml
blob: 044075148edc79893b47827a5886aa9ce0f2e594 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
(* 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 <<THATSALL\n\
            S-1-0\n\
            S-1-42-1337-2187\n\
            THATSALL\n\
            SID                            IDAUTH          NSUB SUBAUTHS\n\
            S-1-0                          0               0    \n\
            S-1-42-1337-2187               42              2    1337 2187"
    ]
  in
  Term.(const sidparse $ arg_validate $ arg_quiet $ arg_sid),
  Term.info ~doc ~man ~exits "sidparse"

let () =
  Term.eval (cmd_sidparse)
  |> Term.exit_status
;;