Skip to content

Commit f1a520d

Browse files
committed
CA-356901: Escape subject before perform ldap query
Signed-off-by: Lin Liu <[email protected]>
1 parent 1809cd2 commit f1a520d

File tree

3 files changed

+104
-41
lines changed

3 files changed

+104
-41
lines changed

ocaml/tests/test_extauth_plugin_ADwinbind.ml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,28 @@ let test_domainify_uname =
112112
|> List.map @@ fun (inp, exp) ->
113113
(Printf.sprintf "%s -> %s" inp exp, `Quick, check inp exp)
114114

115+
let test_ldap_escape =
116+
let open Extauth_plugin_ADwinbind.Ldap in
117+
let check str exp () =
118+
let msg = Printf.sprintf "%s -> %s" str exp in
119+
let escaped = escape str in
120+
Alcotest.(check string) msg exp escaped
121+
in
122+
let matrix =
123+
[
124+
({|user|}, {|user|})
125+
; ({|(user)|}, {|\28user\29|})
126+
; ({|(user|}, {|\28user|})
127+
; ({|user)|}, {|user\29|})
128+
; ({|us\er)|}, {|us\5der\29|})
129+
; ({|user)1|}, {|user\291|})
130+
; ({|user*|}, {|user\2a|})
131+
]
132+
in
133+
matrix
134+
|> List.map @@ fun (inp, exp) ->
135+
(Printf.sprintf "%s -> %s" inp exp, `Quick, check inp exp)
136+
115137
let test_parse_wbinfo_uid_info =
116138
let open Extauth_plugin_ADwinbind.Wbinfo in
117139
let string_of_result x =
@@ -424,6 +446,7 @@ let tests =
424446
; ("ADwinbind:test_range", Range.tests)
425447
; ("ADwinbind:test_parse_value_from_pbis", ParseValueFromPbis.tests)
426448
; ("ADwinbind:test_domainify_uname", test_domainify_uname)
449+
; ("ADwinbind:test_ldap_escape", test_ldap_escape)
427450
; ("ADwinbind:test_parse_wbinfo_uid_info", test_parse_wbinfo_uid_info)
428451
; ("ADwinbind:test_parse_ldap_stdout", test_parse_ldap_stdout)
429452
; ( "ADwinbind:test_wbinfo_exception_of_stderr"

ocaml/xapi/extauth_plugin_ADwinbind.ml

Lines changed: 79 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,61 @@ let ntlm_auth uname passwd : (unit, exn) result =
9090
with _ -> Error (auth_ex uname)
9191

9292
let get_domain_info_from_db () =
93-
(fun __context ->
94-
let host = Helpers.get_localhost ~__context in
95-
let service_name =
96-
Db.Host.get_external_auth_service_name ~__context ~self:host
97-
in
98-
let workgroup, netbios_name =
99-
Db.Host.get_external_auth_configuration ~__context ~self:host |> fun l ->
100-
(List.assoc_opt "workgroup" l, List.assoc_opt "netbios_name" l)
101-
in
102-
{service_name; workgroup; netbios_name}
103-
)
104-
|> Server_helpers.exec_with_new_task
105-
"retrieving external auth domain workgroup"
93+
Server_helpers.exec_with_new_task "retrieving external auth domain workgroup"
94+
@@ fun __context ->
95+
let host = Helpers.get_localhost ~__context in
96+
let service_name =
97+
Db.Host.get_external_auth_service_name ~__context ~self:host
98+
in
99+
let workgroup, netbios_name =
100+
Db.Host.get_external_auth_configuration ~__context ~self:host
101+
|> fun config ->
102+
(List.assoc_opt "workgroup" config, List.assoc_opt "netbios_name" config)
103+
in
104+
{service_name; workgroup; netbios_name}
106105

107106
module Ldap = struct
107+
module Escape = struct
108+
(*
109+
* Escape characters according to
110+
* https://docs.microsoft.com/en-gb/windows/win32/adsi/search-filter-syntax?redirectedfrom=MSDN#special-characters
111+
* *)
112+
113+
let reg_star = {|*|} |> Re.str |> Re.compile
114+
115+
let reg_left_bracket = {|(|} |> Re.str |> Re.compile
116+
117+
let reg_right_bracket = {|)|} |> Re.str |> Re.compile
118+
119+
let reg_backward_slash = {|\|} |> Re.str |> Re.compile
120+
121+
let reg_null = "\000" |> Re.str |> Re.compile
122+
123+
let reg_slash = {|/|} |> Re.str |> Re.compile
124+
125+
let escape_map =
126+
[
127+
(* backward slash goes first as others will include backward slash*)
128+
(reg_backward_slash, {|\5d|})
129+
; (reg_star, {|\2a|})
130+
; (reg_left_bracket, {|\28|})
131+
; (reg_right_bracket, {|\29|})
132+
; (reg_null, {|\00|})
133+
; (reg_slash, {|\2f|})
134+
]
135+
136+
let escape str =
137+
List.fold_left
138+
(fun acc element ->
139+
let reg = fst element in
140+
let value = snd element in
141+
Re.replace_string reg ~by:value acc
142+
)
143+
str escape_map
144+
end
145+
146+
let escape str = Escape.escape str
147+
108148
type user = {
109149
name: string
110150
; display_name: string
@@ -223,15 +263,15 @@ module Ldap = struct
223263
; password_expired= logand user_account_control passw_expire_bit <> 0l
224264
}
225265

226-
let env_of_lookup domain_netbios =
266+
let env_of_krb5 domain_netbios =
227267
let domain_krb5_cfg =
228268
Filename.concat domain_krb5_dir
229269
(Printf.sprintf "krb5.conf.%s" domain_netbios)
230270
in
231271
[|Printf.sprintf "KRB5_CONFIG=%s" domain_krb5_cfg|]
232272

233273
let query_user sid domain_netbios kdc =
234-
let env = env_of_lookup domain_netbios in
274+
let env = env_of_krb5 domain_netbios in
235275
let* stdout =
236276
try
237277
(* Query KDC instead of use domain here
@@ -259,7 +299,9 @@ module Ldap = struct
259299

260300
let query_sid ~name ~kdc ~domain_netbios =
261301
let key = "objectSid" in
262-
let env = env_of_lookup domain_netbios in
302+
let env = env_of_krb5 domain_netbios in
303+
let name = escape name in
304+
(* Escape name to avoid injection detection *)
263305
let query = Printf.sprintf "(|(sAMAccountName=%s)(name=%s))" name name in
264306
let args =
265307
[
@@ -676,11 +718,9 @@ let from_config ~name ~err_msg ~config_params =
676718
let all_number_re = Re.Perl.re {|^\d+$|} |> Re.Perl.compile
677719

678720
let get_localhost_name () =
679-
(fun __context ->
680-
Helpers.get_localhost ~__context |> fun host ->
681-
Db.Host.get_hostname ~__context ~self:host
682-
)
683-
|> Server_helpers.exec_with_new_task "retrieving hostname"
721+
Server_helpers.exec_with_new_task "retrieving hostname" @@ fun __context ->
722+
Helpers.get_localhost ~__context |> fun host ->
723+
Db.Host.get_hostname ~__context ~self:host
684724

685725
let assert_hostname_valid ~hostname =
686726
let all_numbers = Re.matches all_number_re hostname <> [] in
@@ -716,13 +756,12 @@ let persist_extauth_config ~domain ~user ~ou_conf ~workgroup ~netbios_name =
716756
]
717757
@ ou_conf
718758
in
719-
(fun __context ->
720-
Helpers.get_localhost ~__context |> fun self ->
721-
Db.Host.set_external_auth_configuration ~__context ~self ~value ;
722-
Db.Host.get_name_label ~__context ~self
723-
|> debug "update external_auth_configuration for host %s"
724-
)
725-
|> Server_helpers.exec_with_new_task "update external_auth_configuration"
759+
Server_helpers.exec_with_new_task "update external_auth_configuration"
760+
@@ fun __context ->
761+
Helpers.get_localhost ~__context |> fun self ->
762+
Db.Host.set_external_auth_configuration ~__context ~self ~value ;
763+
Db.Host.get_name_label ~__context ~self
764+
|> debug "update external_auth_configuration for host %s"
726765

727766
let disable_machine_account ~service_name = function
728767
| Some u, Some p -> (
@@ -884,21 +923,19 @@ module ClosestKdc = struct
884923
Error e
885924

886925
let update_db ~domain ~kdc =
887-
(fun __context ->
888-
let self = Helpers.get_localhost ~__context in
889-
Db.Host.get_external_auth_configuration ~__context ~self |> fun value ->
890-
(domain, kdc) :: List.remove_assoc domain value |> fun value ->
891-
Db.Host.set_external_auth_configuration ~__context ~self ~value
892-
)
893-
|> Server_helpers.exec_with_new_task "update domain closest kdc"
926+
Server_helpers.exec_with_new_task "update domain closest kdc"
927+
@@ fun __context ->
928+
let self = Helpers.get_localhost ~__context in
929+
Db.Host.get_external_auth_configuration ~__context ~self |> fun value ->
930+
(domain, kdc) :: List.remove_assoc domain value |> fun value ->
931+
Db.Host.set_external_auth_configuration ~__context ~self ~value
894932

895933
let from_db domain =
896-
(fun __context ->
897-
let self = Helpers.get_localhost ~__context in
898-
Db.Host.get_external_auth_configuration ~__context ~self
899-
|> List.assoc_opt domain
900-
)
901-
|> Server_helpers.exec_with_new_task "query domain closest kdc"
934+
Server_helpers.exec_with_new_task "query domain closest kdc"
935+
@@ fun __context ->
936+
let self = Helpers.get_localhost ~__context in
937+
Db.Host.get_external_auth_configuration ~__context ~self
938+
|> List.assoc_opt domain
902939

903940
let lookup domain =
904941
try
@@ -982,6 +1019,7 @@ let closest_kdc_of_domain domain =
9821019

9831020
module AuthADWinbind : Auth_signature.AUTH_MODULE = struct
9841021
let get_subject_identifier' subject_name =
1022+
(* Called in the login path with a yet unauthenticated user *)
9851023
let* domain =
9861024
try Ok (get_domain_info_from_db ()).service_name with e -> Error e
9871025
in

ocaml/xapi/extauth_plugin_ADwinbind.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ module Ldap : sig
6161
val string_of_user : user -> string
6262

6363
val parse_user : string -> (user, string) result
64+
65+
val escape : string -> string
6466
end
6567

6668
module Migrate_from_pbis : sig

0 commit comments

Comments
 (0)