Source file config_gen.ml
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
open! Core
include Config_gen_intf
module Shared = struct
module Format = struct
type t =
{ pre_v5_assumed_realm : string option [@sexp.option]
; host_keytab_path : string option [@sexp.option]
; user_keytab_dir_template : string option [@sexp.option]
; default_domain : string option option [@sexp.option]
; debug_log_config : Debug_log_config.Stable.V1.t option [@sexp.option]
; verbose_errors : bool option [@sexp.option]
; sandboxing_state : [ `None | `Sandboxed | `Exempted ] option [@sexp.option]
; am_sandboxed : bool option [@sexp.option]
}
[@@sexp.allow_extra_fields] [@@deriving of_sexp]
end
type t =
{ pre_v5_assumed_realm : string option [@sexp.option]
; host_keytab_path : string option [@sexp.option]
; user_keytab_dir_template : string option [@sexp.option]
; default_domain : string option option [@sexp.option]
; debug_log_config : Debug_log_config.Stable.V1.t option [@sexp.option]
; verbose_errors : bool option [@sexp.option]
; sandboxing_state : [ `None | `Sandboxed | `Exempted ] option [@sexp.option]
}
[@@deriving fields, sexp_of]
let t_of_sexp sexp =
let { Format.pre_v5_assumed_realm
; host_keytab_path
; user_keytab_dir_template
; default_domain
; debug_log_config
; verbose_errors
; sandboxing_state
; am_sandboxed
}
=
Format.t_of_sexp sexp
in
let sandboxing_state =
match am_sandboxed, sandboxing_state with
| Some _, Some _ ->
raise_s [%message "cannot specify both [am_sandboxed] and [sandboxing_state]"]
| None, None -> None
| Some am_sandboxed, None -> Some (if am_sandboxed then `Sandboxed else `None)
| None, Some sandboxing_state -> Some sandboxing_state
in
{ pre_v5_assumed_realm
; host_keytab_path
; user_keytab_dir_template
; default_domain
; debug_log_config
; verbose_errors
; sandboxing_state
}
;;
let environment_variable = "OCAML_KRB_CONFIG"
let username_template = "%{username}"
let%expect_test "parsing" =
let test str =
let sexp = Sexp.of_string str in
match t_of_sexp sexp with
| t -> sexp_of_t t |> print_s
| exception exn -> print_s [%sexp (exn : Exn.t)]
in
test "((am_sandboxed true))";
[%expect {| ((sandboxing_state Sandboxed)) |}];
test "((am_sandboxed false))";
[%expect {| ((sandboxing_state None)) |}];
test "((sandboxing_state Exempted))";
[%expect {| ((sandboxing_state Exempted)) |}];
test "((sandboxing_state Exempted) (am_sandboxed true))";
[%expect {| "cannot specify both [am_sandboxed] and [sandboxing_state]" |}]
;;
end
include Shared
module type S = S with type t = t
let make ~default ~help_message =
(module struct
include Shared
let field_descriptions () =
let field to_sexp description ac field =
let sexp =
Option.value_map
~default:(Sexp.Atom "<unspecified>")
(Field.get field default)
~f:to_sexp
in
(Field.name field, sexp, description) :: ac
in
let fields =
Fields.fold
~init:[]
~pre_v5_assumed_realm:
(field
[%sexp_of: string]
[ "\n\
When using Protocol V4 and below, clients don't know the realm of \
their peer and assume they are in [pre_v5_assumed_realm]. Protocol V5 \
added support for cross-realm authentication and started sending the \
realm as part of the handshake."
])
~host_keytab_path:
(field
[%sexp_of: string]
[ "\n The path of a keytab specified by [Keytab.Path.Host].\n" ])
~user_keytab_dir_template:
(field
[%sexp_of: string]
[ sprintf
"\n\
\ The path of a keytab specified by [Keytab.Path.User] is determined \
by\n\
\ [filled in user_keytab_dir_template]/$USER.keytab.\n\
\ This must be an absolute path with the substring %s, which will be\n\
\ be filled in with the currently running user.\n"
username_template
])
~default_domain:
(field
[%sexp_of: string option]
[ "\n\
\ The default domain name of hosts in this realm. This value will be \
used to fully qualify hostnames when constructing service principals.\n\n"
])
~debug_log_config:
(field
[%sexp_of: Debug_log_config.Stable.V1.t]
[ sprintf
"\n\
\ Print library debugging information to the outputs specified. The \
following\n\
\ are some example configs:\n\
\ %s\n\
\ "
(List.map
Debug_log_config.examples
~f:Debug_log_config.Stable.V1.sexp_of_t
|> List.map ~f:Sexp.to_string
|> String.concat ~sep:"\n ")
])
~verbose_errors:
(field [%sexp_of: bool] [ "\n Whether error messages should be verbose.\n" ])
~sandboxing_state:(fun acc _ -> acc)
in
String.concat
(List.map
(List.sort fields ~compare:(fun (name1, _, _) (name2, _, _) ->
String.compare name1 name2))
~f:(fun (name, default, description) ->
String.concat
("\n"
:: name
:: " (default "
:: Sexp.to_string default
:: ")"
:: description)))
;;
let help_message () =
let field_descriptions = field_descriptions () in
help_message ~default ~environment_variable ~field_descriptions
;;
let usage () =
eprintf "%s%!" (help_message ());
exit 1
;;
let t =
match Sys.getenv environment_variable with
| None -> default
| Some "" -> usage ()
| Some string ->
(match Result.try_with (fun () -> t_of_sexp (Sexp.of_string string)) with
| Ok t -> t
| Error exn ->
eprintf
"%s\n\n"
(Sexp.to_string_hum
(Error.sexp_of_t
(Error.create
(sprintf
"invalid value for %s environment variable"
environment_variable)
exn
[%sexp_of: exn])));
usage ())
;;
let get_with_default field =
match Option.first_some (Field.get field t) (Field.get field default) with
| None ->
failwithf
"The Kerberos configuration is missing a required field (%s).\n\
Pass the environment variable as described or modify the Config module.\n\n\
===============================================================\n\n\
%s"
(Field.name field)
(help_message ())
()
| Some value -> value
;;
let validate_user_keytab_dir_template x =
let template_occurences =
List.length
(String.substr_index_all x ~may_overlap:false ~pattern:username_template)
in
if not (template_occurences = 1 && Filename.is_absolute x)
then
failwithf
"[user_keytab_dir_template] must be an absolute path with the template %s"
username_template
()
;;
let pre_v5_assumed_realm = get_with_default Fields.pre_v5_assumed_realm
let host_keytab_path = get_with_default Fields.host_keytab_path
let user_keytab_dir_template =
let x = get_with_default Fields.user_keytab_dir_template in
validate_user_keytab_dir_template x;
x
;;
let user_keytab_dir ~username =
String.substr_replace_all
user_keytab_dir_template
~pattern:username_template
~with_:username
;;
let default_domain = get_with_default Fields.default_domain
let debug_log_config = get_with_default Fields.debug_log_config
let verbose_errors = get_with_default Fields.verbose_errors
let sandboxing_state = get_with_default Fields.sandboxing_state
let print_debug_messages = List.length debug_log_config > 0
let am_sandboxed =
match sandboxing_state with
| `Sandboxed -> true
| `None | `Exempted -> false
;;
let am_exempt_from_sandbox =
match sandboxing_state with
| `Exempted -> true
| `None | `Sandboxed -> false
;;
let t =
{ pre_v5_assumed_realm = Some pre_v5_assumed_realm
; host_keytab_path = Some host_keytab_path
; user_keytab_dir_template = Some user_keytab_dir_template
; default_domain = Some default_domain
; debug_log_config = Some debug_log_config
; verbose_errors = Some verbose_errors
; sandboxing_state = Some sandboxing_state
}
;;
end : S)
;;