2 Unix SMB/CIFS implementation.
4 CLDAP server - netlogon handling
6 Copyright (C) Andrew Tridgell 2005
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include <ldb_errors.h>
26 #include "lib/events/events.h"
27 #include "smbd/service_task.h"
28 #include "cldap_server/cldap_server.h"
29 #include "librpc/gen_ndr/ndr_misc.h"
30 #include "libcli/ldap/ldap_ndr.h"
31 #include "libcli/security/security.h"
32 #include "dsdb/samdb/samdb.h"
33 #include "auth/auth.h"
35 #include "system/network.h"
36 #include "lib/socket/netif.h"
37 #include "param/param.h"
38 #include "../lib/tsocket/tsocket.h"
39 #include "libds/common/flag_mapping.h"
40 #include "lib/util/util_net.h"
43 fill in the cldap netlogon union for a given version
45 NTSTATUS
fill_netlogon_samlogon_response(struct ldb_context
*sam_ctx
,
48 const char *netbios_domain
,
49 struct dom_sid
*domain_sid
,
50 const char *domain_guid
,
52 uint32_t acct_control
,
53 const char *src_address
,
55 struct loadparm_context
*lp_ctx
,
56 struct netlogon_samlogon_response
*netlogon
,
57 bool fill_on_blank_request
)
59 const char *dom_attrs
[] = {"objectGUID", NULL
};
60 const char *none_attrs
[] = {NULL
};
61 struct ldb_result
*dom_res
= NULL
, *user_res
= NULL
;
63 const char **services
= lpcfg_server_services(lp_ctx
);
66 struct GUID domain_uuid
;
67 const char *dns_domain
;
68 const char *forest_domain
;
69 const char *pdc_dns_name
;
71 const char *server_site
;
72 const char *client_site
;
74 struct ldb_dn
*domain_dn
= NULL
;
75 struct interface
*ifaces
;
76 bool user_known
= false, am_rodc
= false;
79 /* the domain parameter could have an optional trailing "." */
80 if (domain
&& domain
[strlen(domain
)-1] == '.') {
81 domain
= talloc_strndup(mem_ctx
, domain
, strlen(domain
)-1);
82 NT_STATUS_HAVE_NO_MEMORY(domain
);
85 /* Lookup using long or short domainname */
86 if (domain
&& (strcasecmp_m(domain
, lpcfg_dnsdomain(lp_ctx
)) == 0)) {
87 domain_dn
= ldb_get_default_basedn(sam_ctx
);
89 if (netbios_domain
&& (strcasecmp_m(netbios_domain
, lpcfg_sam_name(lp_ctx
)) == 0)) {
90 domain_dn
= ldb_get_default_basedn(sam_ctx
);
93 const char *domain_identifier
= domain
!= NULL
? domain
95 ret
= ldb_search(sam_ctx
, mem_ctx
, &dom_res
,
96 domain_dn
, LDB_SCOPE_BASE
, dom_attrs
,
97 "objectClass=domain");
98 if (ret
!= LDB_SUCCESS
) {
99 DEBUG(2,("Error finding domain '%s'/'%s' in sam: %s\n",
101 ldb_dn_get_linearized(domain_dn
),
102 ldb_errstring(sam_ctx
)));
103 return NT_STATUS_NO_SUCH_DOMAIN
;
105 if (dom_res
->count
!= 1) {
106 DEBUG(2,("Error finding domain '%s'/'%s' in sam\n",
108 ldb_dn_get_linearized(domain_dn
)));
109 return NT_STATUS_NO_SUCH_DOMAIN
;
113 /* Lookup using GUID or SID */
114 if ((dom_res
== NULL
) && (domain_guid
|| domain_sid
)) {
116 struct GUID binary_guid
;
117 struct ldb_val guid_val
;
119 /* By this means, we ensure we don't have funny stuff in the GUID */
121 status
= GUID_from_string(domain_guid
, &binary_guid
);
122 if (!NT_STATUS_IS_OK(status
)) {
126 /* And this gets the result into the binary format we want anyway */
127 status
= GUID_to_ndr_blob(&binary_guid
, mem_ctx
, &guid_val
);
128 if (!NT_STATUS_IS_OK(status
)) {
131 ret
= ldb_search(sam_ctx
, mem_ctx
, &dom_res
,
132 NULL
, LDB_SCOPE_SUBTREE
,
134 "(&(objectCategory=DomainDNS)(objectGUID=%s))",
135 ldb_binary_encode(mem_ctx
, guid_val
));
136 } else { /* domain_sid case */
137 ret
= ldb_search(sam_ctx
, mem_ctx
, &dom_res
,
138 NULL
, LDB_SCOPE_SUBTREE
,
140 "(&(objectCategory=DomainDNS)(objectSid=%s))",
141 dom_sid_string(mem_ctx
, domain_sid
));
144 if (ret
!= LDB_SUCCESS
) {
145 DEBUG(2,("Unable to find a correct reference to GUID '%s' or SID '%s' in sam: %s\n",
146 domain_guid
, dom_sid_string(mem_ctx
, domain_sid
),
147 ldb_errstring(sam_ctx
)));
148 return NT_STATUS_NO_SUCH_DOMAIN
;
149 } else if (dom_res
->count
== 1) {
150 /* Ok, now just check it is our domain */
151 if (ldb_dn_compare(ldb_get_default_basedn(sam_ctx
),
152 dom_res
->msgs
[0]->dn
) != 0) {
153 DEBUG(2,("The GUID '%s' or SID '%s' doesn't identify our domain\n",
155 dom_sid_string(mem_ctx
, domain_sid
)));
156 return NT_STATUS_NO_SUCH_DOMAIN
;
159 DEBUG(2,("Unable to find a correct reference to GUID '%s' or SID '%s' in sam\n",
160 domain_guid
, dom_sid_string(mem_ctx
, domain_sid
)));
161 return NT_STATUS_NO_SUCH_DOMAIN
;
165 if (dom_res
== NULL
&& fill_on_blank_request
) {
166 /* blank inputs gives our domain - tested against
167 w2k8r2. Without this ADUC on Win7 won't start */
168 domain_dn
= ldb_get_default_basedn(sam_ctx
);
169 ret
= ldb_search(sam_ctx
, mem_ctx
, &dom_res
,
170 domain_dn
, LDB_SCOPE_BASE
, dom_attrs
,
171 "objectClass=domain");
172 if (ret
!= LDB_SUCCESS
) {
173 DEBUG(2,("Error finding domain '%s'/'%s' in sam: %s\n",
174 lpcfg_dnsdomain(lp_ctx
),
175 ldb_dn_get_linearized(domain_dn
),
176 ldb_errstring(sam_ctx
)));
177 return NT_STATUS_NO_SUCH_DOMAIN
;
181 if (dom_res
== NULL
) {
182 DEBUG(2,(__location__
": Unable to get domain information with no inputs\n"));
183 return NT_STATUS_NO_SUCH_DOMAIN
;
186 /* work around different inputs for not-specified users */
191 /* Enquire about any valid username with just a CLDAP packet -
192 * if kerberos didn't also do this, the security folks would
195 /* Only allow some bits to be enquired: [MS-ATDS] 7.3.3.2 */
196 if (acct_control
== (uint32_t)-1) {
199 acct_control
= acct_control
& (ACB_TEMPDUP
| ACB_NORMAL
| ACB_DOMTRUST
| ACB_WSTRUST
| ACB_SVRTRUST
);
201 /* We must exclude disabled accounts, but otherwise do the bitwise match the client asked for */
202 ret
= ldb_search(sam_ctx
, mem_ctx
, &user_res
,
203 dom_res
->msgs
[0]->dn
, LDB_SCOPE_SUBTREE
,
205 "(&(objectClass=user)(samAccountName=%s)"
206 "(!(userAccountControl:" LDB_OID_COMPARATOR_AND
":=%u))"
207 "(userAccountControl:" LDB_OID_COMPARATOR_OR
":=%u))",
208 ldb_binary_encode_string(mem_ctx
, user
),
209 UF_ACCOUNTDISABLE
, ds_acb2uf(acct_control
));
210 if (ret
!= LDB_SUCCESS
) {
211 DEBUG(2,("Unable to find reference to user '%s' with ACB 0x%8x under %s: %s\n",
212 user
, acct_control
, ldb_dn_get_linearized(dom_res
->msgs
[0]->dn
),
213 ldb_errstring(sam_ctx
)));
214 return NT_STATUS_NO_SUCH_USER
;
215 } else if (user_res
->count
== 1) {
225 server_type
= DS_SERVER_DS
;
227 if (samdb_is_pdc(sam_ctx
)) {
228 server_type
|= DS_SERVER_PDC
;
231 if (samdb_is_gc(sam_ctx
)) {
232 server_type
|= DS_SERVER_GC
;
235 if (str_list_check(services
, "ldap")) {
236 server_type
|= DS_SERVER_LDAP
;
239 if (str_list_check(services
, "kdc")) {
240 server_type
|= DS_SERVER_KDC
;
243 if (str_list_check(services
, "ntp_signd")) {
244 server_type
|= DS_SERVER_TIMESERV
| DS_SERVER_GOOD_TIMESERV
;
247 if (samdb_rodc(sam_ctx
, &am_rodc
) == LDB_SUCCESS
&& !am_rodc
) {
248 server_type
|= DS_SERVER_WRITABLE
;
251 if (dsdb_functional_level(sam_ctx
) >= DS_DOMAIN_FUNCTION_2008
) {
252 if (server_type
& DS_SERVER_WRITABLE
) {
253 server_type
|= DS_SERVER_FULL_SECRET_DOMAIN_6
;
255 server_type
|= DS_SERVER_SELECT_SECRET_DOMAIN_6
;
259 if (version
& (NETLOGON_NT_VERSION_5EX
|NETLOGON_NT_VERSION_5EX_WITH_IP
)) {
260 pdc_name
= lpcfg_netbios_name(lp_ctx
);
262 pdc_name
= talloc_asprintf(mem_ctx
, "\\\\%s",
263 lpcfg_netbios_name(lp_ctx
));
264 NT_STATUS_HAVE_NO_MEMORY(pdc_name
);
266 domain_uuid
= samdb_result_guid(dom_res
->msgs
[0], "objectGUID");
267 dns_domain
= lpcfg_dnsdomain(lp_ctx
);
268 forest_domain
= samdb_forest_name(sam_ctx
, mem_ctx
);
269 NT_STATUS_HAVE_NO_MEMORY(forest_domain
);
270 pdc_dns_name
= talloc_asprintf(mem_ctx
, "%s.%s",
271 strlower_talloc(mem_ctx
,
272 lpcfg_netbios_name(lp_ctx
)),
274 NT_STATUS_HAVE_NO_MEMORY(pdc_dns_name
);
275 flatname
= lpcfg_workgroup(lp_ctx
);
277 server_site
= samdb_server_site_name(sam_ctx
, mem_ctx
);
278 NT_STATUS_HAVE_NO_MEMORY(server_site
);
279 client_site
= samdb_client_site_name(sam_ctx
, mem_ctx
,
281 NT_STATUS_HAVE_NO_MEMORY(client_site
);
282 if (strcasecmp(server_site
, client_site
) == 0) {
283 server_type
|= DS_SERVER_CLOSEST
;
286 load_interface_list(mem_ctx
, lp_ctx
, &ifaces
);
288 pdc_ip
= iface_list_best_ip(ifaces
, src_address
);
290 pdc_ip
= iface_list_first_v4(ifaces
);
292 if (pdc_ip
== NULL
|| !is_ipaddress_v4(pdc_ip
)) {
293 /* this matches windows behaviour */
294 pdc_ip
= "127.0.0.1";
297 ZERO_STRUCTP(netlogon
);
299 /* check if either of these bits is present */
300 if (version
& (NETLOGON_NT_VERSION_5EX
|NETLOGON_NT_VERSION_5EX_WITH_IP
)) {
301 uint32_t extra_flags
= 0;
302 netlogon
->ntver
= NETLOGON_NT_VERSION_5EX
;
304 /* could check if the user exists */
306 netlogon
->data
.nt5_ex
.command
= LOGON_SAM_LOGON_RESPONSE_EX
;
308 netlogon
->data
.nt5_ex
.command
= LOGON_SAM_LOGON_USER_UNKNOWN_EX
;
310 netlogon
->data
.nt5_ex
.pdc_name
= pdc_name
;
311 netlogon
->data
.nt5_ex
.user_name
= user
;
312 netlogon
->data
.nt5_ex
.domain_name
= flatname
;
313 netlogon
->data
.nt5_ex
.domain_uuid
= domain_uuid
;
314 netlogon
->data
.nt5_ex
.forest
= forest_domain
;
315 netlogon
->data
.nt5_ex
.dns_domain
= dns_domain
;
316 netlogon
->data
.nt5_ex
.pdc_dns_name
= pdc_dns_name
;
317 netlogon
->data
.nt5_ex
.server_site
= server_site
;
318 netlogon
->data
.nt5_ex
.client_site
= client_site
;
319 if (version
& NETLOGON_NT_VERSION_5EX_WITH_IP
) {
320 /* note that this is always a IPV4 address */
321 extra_flags
= NETLOGON_NT_VERSION_5EX_WITH_IP
;
322 netlogon
->data
.nt5_ex
.sockaddr
.sockaddr_family
= 2;
323 netlogon
->data
.nt5_ex
.sockaddr
.pdc_ip
= pdc_ip
;
324 netlogon
->data
.nt5_ex
.sockaddr
.remaining
= data_blob_talloc_zero(mem_ctx
, 8);
326 netlogon
->data
.nt5_ex
.server_type
= server_type
;
327 netlogon
->data
.nt5_ex
.nt_version
= NETLOGON_NT_VERSION_1
|NETLOGON_NT_VERSION_5EX
|extra_flags
;
328 netlogon
->data
.nt5_ex
.lmnt_token
= 0xFFFF;
329 netlogon
->data
.nt5_ex
.lm20_token
= 0xFFFF;
331 } else if (version
& NETLOGON_NT_VERSION_5
) {
332 netlogon
->ntver
= NETLOGON_NT_VERSION_5
;
334 /* could check if the user exists */
336 netlogon
->data
.nt5
.command
= LOGON_SAM_LOGON_RESPONSE
;
338 netlogon
->data
.nt5
.command
= LOGON_SAM_LOGON_USER_UNKNOWN
;
340 netlogon
->data
.nt5
.pdc_name
= pdc_name
;
341 netlogon
->data
.nt5
.user_name
= user
;
342 netlogon
->data
.nt5
.domain_name
= flatname
;
343 netlogon
->data
.nt5
.domain_uuid
= domain_uuid
;
344 netlogon
->data
.nt5
.forest
= forest_domain
;
345 netlogon
->data
.nt5
.dns_domain
= dns_domain
;
346 netlogon
->data
.nt5
.pdc_dns_name
= pdc_dns_name
;
347 netlogon
->data
.nt5
.pdc_ip
= pdc_ip
;
348 netlogon
->data
.nt5
.server_type
= server_type
;
349 netlogon
->data
.nt5
.nt_version
= NETLOGON_NT_VERSION_1
|NETLOGON_NT_VERSION_5
;
350 netlogon
->data
.nt5
.lmnt_token
= 0xFFFF;
351 netlogon
->data
.nt5
.lm20_token
= 0xFFFF;
353 } else /* (version & NETLOGON_NT_VERSION_1) and all other cases */ {
354 netlogon
->ntver
= NETLOGON_NT_VERSION_1
;
355 /* could check if the user exists */
357 netlogon
->data
.nt4
.command
= LOGON_SAM_LOGON_RESPONSE
;
359 netlogon
->data
.nt4
.command
= LOGON_SAM_LOGON_USER_UNKNOWN
;
361 netlogon
->data
.nt4
.pdc_name
= pdc_name
;
362 netlogon
->data
.nt4
.user_name
= user
;
363 netlogon
->data
.nt4
.domain_name
= flatname
;
364 netlogon
->data
.nt4
.nt_version
= NETLOGON_NT_VERSION_1
;
365 netlogon
->data
.nt4
.lmnt_token
= 0xFFFF;
366 netlogon
->data
.nt4
.lm20_token
= 0xFFFF;
374 handle incoming cldap requests
376 void cldapd_netlogon_request(struct cldap_socket
*cldap
,
377 struct cldapd_server
*cldapd
,
380 struct ldb_parse_tree
*tree
,
381 struct tsocket_address
*src
)
384 const char *domain
= NULL
;
385 const char *host
= NULL
;
386 const char *user
= NULL
;
387 const char *domain_guid
= NULL
;
388 struct dom_sid
*domain_sid
= NULL
;
389 int acct_control
= -1;
391 struct netlogon_samlogon_response netlogon
;
392 NTSTATUS status
= NT_STATUS_INVALID_PARAMETER
;
394 if (tree
->operation
!= LDB_OP_AND
) goto failed
;
396 /* extract the query elements */
397 for (i
=0;i
<tree
->u
.list
.num_elements
;i
++) {
398 struct ldb_parse_tree
*t
= tree
->u
.list
.elements
[i
];
399 if (t
->operation
!= LDB_OP_EQUALITY
) goto failed
;
400 if (strcasecmp(t
->u
.equality
.attr
, "DnsDomain") == 0) {
401 domain
= talloc_strndup(tmp_ctx
,
402 (const char *)t
->u
.equality
.value
.data
,
403 t
->u
.equality
.value
.length
);
405 if (strcasecmp(t
->u
.equality
.attr
, "Host") == 0) {
406 host
= talloc_strndup(tmp_ctx
,
407 (const char *)t
->u
.equality
.value
.data
,
408 t
->u
.equality
.value
.length
);
410 if (strcasecmp(t
->u
.equality
.attr
, "DomainGuid") == 0) {
413 enc_status
= ldap_decode_ndr_GUID(tmp_ctx
,
414 t
->u
.equality
.value
, &guid
);
415 if (NT_STATUS_IS_OK(enc_status
)) {
416 domain_guid
= GUID_string(tmp_ctx
, &guid
);
419 if (strcasecmp(t
->u
.equality
.attr
, "DomainSid") == 0) {
420 enum ndr_err_code ndr_err
;
422 domain_sid
= talloc(tmp_ctx
, struct dom_sid
);
423 if (domain_sid
== NULL
) {
426 ndr_err
= ndr_pull_struct_blob(&t
->u
.equality
.value
,
427 domain_sid
, domain_sid
,
428 (ndr_pull_flags_fn_t
)ndr_pull_dom_sid
);
429 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
430 talloc_free(domain_sid
);
434 if (strcasecmp(t
->u
.equality
.attr
, "User") == 0) {
435 user
= talloc_strndup(tmp_ctx
,
436 (const char *)t
->u
.equality
.value
.data
,
437 t
->u
.equality
.value
.length
);
439 if (strcasecmp(t
->u
.equality
.attr
, "NtVer") == 0 &&
440 t
->u
.equality
.value
.length
== 4) {
441 version
= IVAL(t
->u
.equality
.value
.data
, 0);
443 if (strcasecmp(t
->u
.equality
.attr
, "AAC") == 0 &&
444 t
->u
.equality
.value
.length
== 4) {
445 acct_control
= IVAL(t
->u
.equality
.value
.data
, 0);
449 if ((domain
== NULL
) && (domain_guid
== NULL
) && (domain_sid
== NULL
)) {
450 domain
= lpcfg_dnsdomain(cldapd
->task
->lp_ctx
);
457 DEBUG(5,("cldap netlogon query domain=%s host=%s user=%s version=%d guid=%s\n",
458 domain
, host
, user
, version
, domain_guid
));
460 status
= fill_netlogon_samlogon_response(cldapd
->samctx
, tmp_ctx
,
461 domain
, NULL
, domain_sid
,
464 tsocket_address_inet_addr_string(src
, tmp_ctx
),
465 version
, cldapd
->task
->lp_ctx
,
467 if (!NT_STATUS_IS_OK(status
)) {
471 status
= cldap_netlogon_reply(cldap
, message_id
, src
, version
, &netlogon
);
472 if (!NT_STATUS_IS_OK(status
)) {
479 DEBUG(2,("cldap netlogon query failed domain=%s host=%s version=%d - %s\n",
480 domain
, host
, version
, nt_errstr(status
)));
481 cldap_empty_reply(cldap
, message_id
, src
);