@@ -62,6 +62,14 @@ let domain_krb5_dir = Filename.concat Xapi_globs.samba_dir "lock/smb_krb5"
6262
6363let debug_level () = ! Xapi_globs. winbind_debug_level |> string_of_int
6464
65+ type domain_info = {
66+ service_name : string
67+ ; workgroup : string option
68+ (* For upgrade case, the legacy db does not contain workgroup *)
69+ ; netbios_name : string option
70+ (* Persist netbios_name to support hostname change *)
71+ }
72+
6573let hd msg = function
6674 | [] ->
6775 error " %s" msg ;
@@ -81,7 +89,62 @@ let ntlm_auth uname passwd : (unit, exn) result =
8189 Ok ()
8290 with _ -> Error (auth_ex uname)
8391
92+ let get_domain_info_from_db () =
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}
105+
84106module 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, {| \5 d| })
129+ ; (reg_star, {| \2 a| })
130+ ; (reg_left_bracket, {| \28 | })
131+ ; (reg_right_bracket, {| \29 | })
132+ ; (reg_null, {| \00 | })
133+ ; (reg_slash, {| \2 f| })
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+
85148 type user = {
86149 name : string
87150 ; display_name : string
@@ -200,12 +263,15 @@ module Ldap = struct
200263 ; password_expired= logand user_account_control passw_expire_bit <> 0l
201264 }
202265
203- let query_user sid domain_netbios kdc =
266+ let env_of_krb5 domain_netbios =
204267 let domain_krb5_cfg =
205268 Filename. concat domain_krb5_dir
206269 (Printf. sprintf " krb5.conf.%s" domain_netbios)
207270 in
208- let env = [|Printf. sprintf " KRB5_CONFIG=%s" domain_krb5_cfg|] in
271+ [|Printf. sprintf " KRB5_CONFIG=%s" domain_krb5_cfg|]
272+
273+ let query_user sid domain_netbios kdc =
274+ let env = env_of_krb5 domain_netbios in
209275 let * stdout =
210276 try
211277 (* Query KDC instead of use domain here
@@ -227,9 +293,40 @@ module Ldap = struct
227293 args
228294 in
229295 Ok stdout
230- with _ -> Error (generic_ex " ldap query failed" )
296+ with _ -> Error (generic_ex " ldap query user info from sid failed" )
231297 in
232298 parse_user stdout < ! > generic_ex " %s"
299+
300+ let query_sid ~name ~kdc ~domain_netbios =
301+ let key = " objectSid" in
302+ let env = env_of_krb5 domain_netbios in
303+ let name = escape name in
304+ (* Escape name to avoid injection detection *)
305+ let query = Printf. sprintf " (|(sAMAccountName=%s)(name=%s))" name name in
306+ let args =
307+ [
308+ " ads"
309+ ; " search"
310+ ; " -d"
311+ ; debug_level ()
312+ ; " --server"
313+ ; kdc
314+ ; " --machine-pass"
315+ ; query
316+ ; key
317+ ]
318+ in
319+ try
320+ Helpers. call_script ~env ! Xapi_globs. net_cmd args
321+ |> Xapi_cmd_result. of_output ~sep: ':' ~key
322+ |> fun x -> Ok x
323+ with
324+ | Forkhelpers. Spawn_internal_error (_ , stdout , _ ) ->
325+ Error (generic_ex " Ldap query sid failed: %s" stdout)
326+ | Not_found ->
327+ Error (generic_ex " %s not found in ldap result" key)
328+ | _ ->
329+ Error (generic_ex " Failed to lookup sid from username %s" name)
233330end
234331
235332type domain_name_type = Name | NetbiosName
@@ -345,6 +442,25 @@ module Wbinfo = struct
345442 | _ ->
346443 Error (generic_ex " Invalid domain user name %s" uname)
347444
445+ let domain_and_user_of_uname uname =
446+ let open Astring.String in
447+ match String. split_on_char '\\' uname with
448+ | [netbios; user] ->
449+ let * domain =
450+ domain_name_of ~target_name_type: Name ~from_name: netbios
451+ in
452+ Ok (domain, user)
453+ | _ -> (
454+ match String. split_on_char '@' uname with
455+ | [user; domain] ->
456+ Ok (domain, user)
457+ | _ ->
458+ if is_infix ~affix: " @" uname || is_infix ~affix: {| \| } uname then
459+ Error (generic_ex " Invalid domain user name %s" uname)
460+ else
461+ Ok ((get_domain_info_from_db () ).service_name, uname)
462+ )
463+
348464 let all_domain_netbios () =
349465 (*
350466 * List all domains (trusted and own domain)
@@ -445,14 +561,6 @@ module Wbinfo = struct
445561 parse_uid_info stdout < ! > fun () -> parsing_ex args
446562end
447563
448- type domain_info = {
449- service_name : string
450- ; workgroup : string option
451- (* For upgrade case, the legacy db does not contain workgroup *)
452- ; netbios_name : string option
453- (* Persist netbios_name to support hostname change *)
454- }
455-
456564module Migrate_from_pbis = struct
457565 (* upgrade-pbis-to-winbind handles most of the migration from PBIS database
458566 * to winbind database
@@ -532,21 +640,6 @@ module Migrate_from_pbis = struct
532640 netbios_name
533641end
534642
535- let get_domain_info_from_db () =
536- (fun __context ->
537- let host = Helpers. get_localhost ~__context in
538- let service_name =
539- Db.Host. get_external_auth_service_name ~__context ~self: host
540- in
541- let workgroup, netbios_name =
542- Db.Host. get_external_auth_configuration ~__context ~self: host |> fun l ->
543- (List. assoc_opt " workgroup" l, List. assoc_opt " netbios_name" l)
544- in
545- {service_name; workgroup; netbios_name}
546- )
547- |> Server_helpers. exec_with_new_task
548- " retrieving external auth domain workgroup"
549-
550643let kdcs_of_domain domain =
551644 try
552645 Helpers. call_script ~log_output: On_failure net_cmd
@@ -625,11 +718,9 @@ let from_config ~name ~err_msg ~config_params =
625718let all_number_re = Re.Perl. re {|^ \d+$| } |> Re.Perl. compile
626719
627720let get_localhost_name () =
628- (fun __context ->
629- Helpers. get_localhost ~__context |> fun host ->
630- Db.Host. get_hostname ~__context ~self: host
631- )
632- |> 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
633724
634725let assert_hostname_valid ~hostname =
635726 let all_numbers = Re. matches all_number_re hostname <> [] in
@@ -665,13 +756,12 @@ let persist_extauth_config ~domain ~user ~ou_conf ~workgroup ~netbios_name =
665756 ]
666757 @ ou_conf
667758 in
668- (fun __context ->
669- Helpers. get_localhost ~__context |> fun self ->
670- Db.Host. set_external_auth_configuration ~__context ~self ~value ;
671- Db.Host. get_name_label ~__context ~self
672- |> debug " update external_auth_configuration for host %s"
673- )
674- |> 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"
675765
676766let disable_machine_account ~service_name = function
677767 | Some u , Some p -> (
@@ -833,21 +923,19 @@ module ClosestKdc = struct
833923 Error e
834924
835925 let update_db ~domain ~kdc =
836- (fun __context ->
837- let self = Helpers. get_localhost ~__context in
838- Db.Host. get_external_auth_configuration ~__context ~self |> fun value ->
839- (domain, kdc) :: List. remove_assoc domain value |> fun value ->
840- Db.Host. set_external_auth_configuration ~__context ~self ~value
841- )
842- |> 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
843932
844933 let from_db domain =
845- (fun __context ->
846- let self = Helpers. get_localhost ~__context in
847- Db.Host. get_external_auth_configuration ~__context ~self
848- |> List. assoc_opt domain
849- )
850- |> 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
851939
852940 let lookup domain =
853941 try
@@ -921,13 +1009,33 @@ let build_dns_hostname_option ~config_params =
9211009 | _ ->
9221010 []
9231011
1012+ let closest_kdc_of_domain domain =
1013+ match ClosestKdc. from_db domain with
1014+ | Some kdc ->
1015+ kdc
1016+ | None ->
1017+ (* Just pick the first KDC in the list *)
1018+ kdc_of_domain domain
1019+
9241020module AuthADWinbind : Auth_signature .AUTH_MODULE = struct
9251021 let get_subject_identifier' subject_name =
1022+ (* Called in the login path with a yet unauthenticated user *)
9261023 let * domain =
9271024 try Ok (get_domain_info_from_db () ).service_name with e -> Error e
9281025 in
9291026 let subject_name = domainify_uname ~domain subject_name in
930- Wbinfo. sid_of_name subject_name
1027+ match Wbinfo. sid_of_name subject_name with
1028+ | Ok sid ->
1029+ Ok sid
1030+ | Error e ->
1031+ debug " Failed to query sid from cache, error: %s, retry ldap"
1032+ (ExnHelper. string_of_exn e) ;
1033+ let * domain, user = Wbinfo. domain_and_user_of_uname subject_name in
1034+ let * domain_netbios =
1035+ Wbinfo. domain_name_of ~target_name_type: NetbiosName ~from_name: domain
1036+ in
1037+ let kdc = closest_kdc_of_domain domain in
1038+ Ldap. query_sid ~name: user ~kdc ~domain_netbios
9311039
9321040 (* subject_id get_subject_identifier(string subject_name)
9331041
@@ -1004,14 +1112,7 @@ module AuthADWinbind : Auth_signature.AUTH_MODULE = struct
10041112 let query_subject_information_user (uid : int ) (sid : string ) =
10051113 let * {user_name; gecos; gid} = Wbinfo. uid_info_of_uid uid in
10061114 let * domain_netbios, domain = Wbinfo. domain_of_uname user_name in
1007- let closest_kdc =
1008- match ClosestKdc. from_db domain with
1009- | Some kdc ->
1010- kdc
1011- | None ->
1012- (* Just pick the first KDC in the list *)
1013- kdc_of_domain domain
1014- in
1115+ let closest_kdc = closest_kdc_of_domain domain in
10151116 let * {
10161117 name
10171118 ; upn
0 commit comments