2 Unix SMB/CIFS implementation.
4 CLDAP server - netlogon handling
6 Copyright (C) Andrew Tridgell 2005
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "libcli/ldap/ldap_ndr.h"
24 #include "lib/ldb/include/ldb.h"
25 #include "lib/ldb/include/ldb_errors.h"
26 #include "lib/events/events.h"
27 #include "lib/socket/socket.h"
28 #include "smbd/service_task.h"
29 #include "cldap_server/cldap_server.h"
30 #include "librpc/gen_ndr/ndr_misc.h"
31 #include "dsdb/samdb/samdb.h"
32 #include "auth/auth.h"
34 #include "system/network.h"
35 #include "lib/socket/netif.h"
36 #include "param/param.h"
39 fill in the cldap netlogon union for a given version
41 static NTSTATUS
cldapd_netlogon_fill(struct cldapd_server
*cldapd
,
44 const char *domain_guid
,
46 const char *src_address
,
48 struct loadparm_context
*lp_ctx
,
49 union nbt_cldap_netlogon
*netlogon
)
51 const char *ref_attrs
[] = {"nETBIOSName", "dnsRoot", "ncName", NULL
};
52 const char *dom_attrs
[] = {"objectGUID", NULL
};
53 struct ldb_result
*ref_res
= NULL
, *dom_res
= NULL
;
55 const char **services
= lp_server_services(lp_ctx
);
58 struct GUID domain_uuid
;
60 const char *dns_domain
;
61 const char *pdc_dns_name
;
63 const char *server_site
;
64 const char *client_site
;
66 struct ldb_dn
*partitions_basedn
;
67 struct interface
*ifaces
;
69 partitions_basedn
= samdb_partitions_dn(cldapd
->samctx
, mem_ctx
);
71 /* the domain has an optional trailing . */
72 if (domain
&& domain
[strlen(domain
)-1] == '.') {
73 domain
= talloc_strndup(mem_ctx
, domain
, strlen(domain
)-1);
77 struct ldb_dn
*dom_dn
;
78 /* try and find the domain */
80 ret
= ldb_search_exp_fmt(cldapd
->samctx
, mem_ctx
, &ref_res
,
81 partitions_basedn
, LDB_SCOPE_ONELEVEL
,
83 "(&(&(objectClass=crossRef)(dnsRoot=%s))(nETBIOSName=*))",
86 if (ret
!= LDB_SUCCESS
) {
87 DEBUG(2,("Unable to find referece to '%s' in sam: %s\n",
89 ldb_errstring(cldapd
->samctx
)));
90 return NT_STATUS_NO_SUCH_DOMAIN
;
91 } else if (ref_res
->count
== 1) {
92 talloc_steal(mem_ctx
, dom_res
);
93 dom_dn
= ldb_msg_find_attr_as_dn(cldapd
->samctx
, mem_ctx
, ref_res
->msgs
[0], "ncName");
95 return NT_STATUS_NO_SUCH_DOMAIN
;
97 ret
= ldb_search(cldapd
->samctx
, dom_dn
,
98 LDB_SCOPE_BASE
, "objectClass=domain",
100 if (ret
!= LDB_SUCCESS
) {
101 DEBUG(2,("Error finding domain '%s'/'%s' in sam: %s\n", domain
, ldb_dn_get_linearized(dom_dn
), ldb_errstring(cldapd
->samctx
)));
102 return NT_STATUS_NO_SUCH_DOMAIN
;
104 talloc_steal(mem_ctx
, dom_res
);
105 if (dom_res
->count
!= 1) {
106 DEBUG(2,("Error finding domain '%s'/'%s' in sam\n", domain
, ldb_dn_get_linearized(dom_dn
)));
107 return NT_STATUS_NO_SUCH_DOMAIN
;
109 } else if (ref_res
->count
> 1) {
110 talloc_free(ref_res
);
111 return NT_STATUS_NO_SUCH_DOMAIN
;
115 if ((dom_res
== NULL
|| dom_res
->count
== 0) && domain_guid
) {
118 ret
= ldb_search_exp_fmt(cldapd
->samctx
, mem_ctx
, &dom_res
,
119 NULL
, LDB_SCOPE_SUBTREE
,
121 "(&(objectClass=domainDNS)(objectGUID=%s))",
124 if (ret
!= LDB_SUCCESS
) {
125 DEBUG(2,("Unable to find referece to GUID '%s' in sam: %s\n",
127 ldb_errstring(cldapd
->samctx
)));
128 return NT_STATUS_NO_SUCH_DOMAIN
;
129 } else if (dom_res
->count
== 1) {
130 /* try and find the domain */
131 ret
= ldb_search_exp_fmt(cldapd
->samctx
, mem_ctx
, &ref_res
,
132 partitions_basedn
, LDB_SCOPE_ONELEVEL
,
134 "(&(objectClass=crossRef)(ncName=%s))",
135 ldb_dn_get_linearized(dom_res
->msgs
[0]->dn
));
137 if (ret
!= LDB_SUCCESS
) {
138 DEBUG(2,("Unable to find referece to '%s' in sam: %s\n",
139 ldb_dn_get_linearized(dom_res
->msgs
[0]->dn
),
140 ldb_errstring(cldapd
->samctx
)));
141 return NT_STATUS_NO_SUCH_DOMAIN
;
143 } else if (ref_res
->count
!= 1) {
144 DEBUG(2,("Unable to find referece to '%s' in sam\n",
145 ldb_dn_get_linearized(dom_res
->msgs
[0]->dn
)));
146 return NT_STATUS_NO_SUCH_DOMAIN
;
148 } else if (dom_res
->count
> 1) {
149 talloc_free(ref_res
);
150 return NT_STATUS_NO_SUCH_DOMAIN
;
154 if ((ref_res
== NULL
|| ref_res
->count
== 0)) {
155 DEBUG(2,("Unable to find domain reference with name %s or GUID {%s}\n", domain
, domain_guid
));
156 return NT_STATUS_NO_SUCH_DOMAIN
;
159 if ((dom_res
== NULL
|| dom_res
->count
== 0)) {
160 DEBUG(2,("Unable to find domain with name %s or GUID {%s}\n", domain
, domain_guid
));
161 return NT_STATUS_NO_SUCH_DOMAIN
;
165 NBT_SERVER_DS
| NBT_SERVER_TIMESERV
|
166 NBT_SERVER_CLOSEST
| NBT_SERVER_WRITABLE
|
167 NBT_SERVER_GOOD_TIMESERV
;
169 if (samdb_is_pdc(cldapd
->samctx
)) {
170 server_type
|= NBT_SERVER_PDC
;
173 if (samdb_is_gc(cldapd
->samctx
)) {
174 server_type
|= NBT_SERVER_GC
;
177 if (str_list_check(services
, "ldap")) {
178 server_type
|= NBT_SERVER_LDAP
;
181 if (str_list_check(services
, "kdc")) {
182 server_type
|= NBT_SERVER_KDC
;
185 pdc_name
= talloc_asprintf(mem_ctx
, "\\\\%s", lp_netbios_name(lp_ctx
));
186 domain_uuid
= samdb_result_guid(dom_res
->msgs
[0], "objectGUID");
187 realm
= samdb_result_string(ref_res
->msgs
[0], "dnsRoot", lp_realm(lp_ctx
));
188 dns_domain
= samdb_result_string(ref_res
->msgs
[0], "dnsRoot", lp_realm(lp_ctx
));
189 pdc_dns_name
= talloc_asprintf(mem_ctx
, "%s.%s",
190 strlower_talloc(mem_ctx
,
191 lp_netbios_name(lp_ctx
)),
194 flatname
= samdb_result_string(ref_res
->msgs
[0], "nETBIOSName",
195 lp_workgroup(lp_ctx
));
196 server_site
= "Default-First-Site-Name";
197 client_site
= "Default-First-Site-Name";
198 load_interfaces(mem_ctx
, lp_interfaces(lp_ctx
), &ifaces
);
199 pdc_ip
= iface_best_ip(ifaces
, src_address
);
201 ZERO_STRUCTP(netlogon
);
203 switch (version
& 0xF) {
206 netlogon
->logon1
.type
= (user
?19+2:19);
207 netlogon
->logon1
.pdc_name
= pdc_name
;
208 netlogon
->logon1
.user_name
= user
;
209 netlogon
->logon1
.domain_name
= flatname
;
210 netlogon
->logon1
.nt_version
= 1;
211 netlogon
->logon1
.lmnt_token
= 0xFFFF;
212 netlogon
->logon1
.lm20_token
= 0xFFFF;
216 netlogon
->logon3
.type
= (user
?19+2:19);
217 netlogon
->logon3
.pdc_name
= pdc_name
;
218 netlogon
->logon3
.user_name
= user
;
219 netlogon
->logon3
.domain_name
= flatname
;
220 netlogon
->logon3
.domain_uuid
= domain_uuid
;
221 netlogon
->logon3
.forest
= realm
;
222 netlogon
->logon3
.dns_domain
= dns_domain
;
223 netlogon
->logon3
.pdc_dns_name
= pdc_dns_name
;
224 netlogon
->logon3
.pdc_ip
= pdc_ip
;
225 netlogon
->logon3
.server_type
= server_type
;
226 netlogon
->logon3
.lmnt_token
= 0xFFFF;
227 netlogon
->logon3
.lm20_token
= 0xFFFF;
233 netlogon
->logon5
.type
= (user
?NETLOGON_RESPONSE_FROM_PDC_USER
:NETLOGON_RESPONSE_FROM_PDC2
);
234 netlogon
->logon5
.server_type
= server_type
;
235 netlogon
->logon5
.domain_uuid
= domain_uuid
;
236 netlogon
->logon5
.forest
= realm
;
237 netlogon
->logon5
.dns_domain
= dns_domain
;
238 netlogon
->logon5
.pdc_dns_name
= pdc_dns_name
;
239 netlogon
->logon5
.domain
= flatname
;
240 netlogon
->logon5
.pdc_name
= lp_netbios_name(lp_ctx
);
241 netlogon
->logon5
.user_name
= user
;
242 netlogon
->logon5
.server_site
= server_site
;
243 netlogon
->logon5
.client_site
= client_site
;
244 netlogon
->logon5
.lmnt_token
= 0xFFFF;
245 netlogon
->logon5
.lm20_token
= 0xFFFF;
248 netlogon
->logon13
.type
= (user
?NETLOGON_RESPONSE_FROM_PDC_USER
:NETLOGON_RESPONSE_FROM_PDC2
);
249 netlogon
->logon13
.server_type
= server_type
;
250 netlogon
->logon13
.domain_uuid
= domain_uuid
;
251 netlogon
->logon13
.forest
= realm
;
252 netlogon
->logon13
.dns_domain
= dns_domain
;
253 netlogon
->logon13
.pdc_dns_name
= pdc_dns_name
;
254 netlogon
->logon13
.domain
= flatname
;
255 netlogon
->logon13
.pdc_name
= lp_netbios_name(lp_ctx
);
256 netlogon
->logon13
.user_name
= user
;
257 netlogon
->logon13
.server_site
= server_site
;
258 netlogon
->logon13
.client_site
= client_site
;
259 netlogon
->logon13
.unknown
= 10;
260 netlogon
->logon13
.unknown2
= 2;
261 netlogon
->logon13
.pdc_ip
= pdc_ip
;
262 netlogon
->logon13
.lmnt_token
= 0xFFFF;
263 netlogon
->logon13
.lm20_token
= 0xFFFF;
272 handle incoming cldap requests
274 void cldapd_netlogon_request(struct cldap_socket
*cldap
,
276 struct ldb_parse_tree
*tree
,
277 struct socket_address
*src
)
279 struct cldapd_server
*cldapd
= talloc_get_type(cldap
->incoming
.private, struct cldapd_server
);
281 const char *domain
= NULL
;
282 const char *host
= NULL
;
283 const char *user
= NULL
;
284 const char *domain_guid
= NULL
;
285 const char *domain_sid
= NULL
;
286 int acct_control
= -1;
288 union nbt_cldap_netlogon netlogon
;
289 NTSTATUS status
= NT_STATUS_INVALID_PARAMETER
;
291 TALLOC_CTX
*tmp_ctx
= talloc_new(cldap
);
293 if (tree
->operation
!= LDB_OP_AND
) goto failed
;
295 /* extract the query elements */
296 for (i
=0;i
<tree
->u
.list
.num_elements
;i
++) {
297 struct ldb_parse_tree
*t
= tree
->u
.list
.elements
[i
];
298 if (t
->operation
!= LDB_OP_EQUALITY
) goto failed
;
299 if (strcasecmp(t
->u
.equality
.attr
, "DnsDomain") == 0) {
300 domain
= talloc_strndup(tmp_ctx
,
301 (const char *)t
->u
.equality
.value
.data
,
302 t
->u
.equality
.value
.length
);
304 if (strcasecmp(t
->u
.equality
.attr
, "Host") == 0) {
305 host
= talloc_strndup(tmp_ctx
,
306 (const char *)t
->u
.equality
.value
.data
,
307 t
->u
.equality
.value
.length
);
309 if (strcasecmp(t
->u
.equality
.attr
, "DomainGuid") == 0) {
312 enc_status
= ldap_decode_ndr_GUID(tmp_ctx
,
313 t
->u
.equality
.value
, &guid
);
314 if (NT_STATUS_IS_OK(enc_status
)) {
315 domain_guid
= GUID_string(tmp_ctx
, &guid
);
318 if (strcasecmp(t
->u
.equality
.attr
, "DomainSid") == 0) {
319 domain_sid
= talloc_strndup(tmp_ctx
,
320 (const char *)t
->u
.equality
.value
.data
,
321 t
->u
.equality
.value
.length
);
323 if (strcasecmp(t
->u
.equality
.attr
, "User") == 0) {
324 user
= talloc_strndup(tmp_ctx
,
325 (const char *)t
->u
.equality
.value
.data
,
326 t
->u
.equality
.value
.length
);
328 if (strcasecmp(t
->u
.equality
.attr
, "NtVer") == 0 &&
329 t
->u
.equality
.value
.length
== 4) {
330 version
= IVAL(t
->u
.equality
.value
.data
, 0);
332 if (strcasecmp(t
->u
.equality
.attr
, "AAC") == 0 &&
333 t
->u
.equality
.value
.length
== 4) {
334 acct_control
= IVAL(t
->u
.equality
.value
.data
, 0);
338 if (domain_guid
== NULL
&& domain
== NULL
) {
339 domain
= lp_realm(cldapd
->task
->lp_ctx
);
346 DEBUG(5,("cldap netlogon query domain=%s host=%s user=%s version=%d guid=%s\n",
347 domain
, host
, user
, version
, domain_guid
));
349 status
= cldapd_netlogon_fill(cldapd
, tmp_ctx
, domain
, domain_guid
,
351 version
, cldapd
->task
->lp_ctx
, &netlogon
);
352 if (!NT_STATUS_IS_OK(status
)) {
356 status
= cldap_netlogon_reply(cldap
, message_id
, src
, version
,
358 if (!NT_STATUS_IS_OK(status
)) {
362 talloc_free(tmp_ctx
);
366 DEBUG(2,("cldap netlogon query failed domain=%s host=%s version=%d - %s\n",
367 domain
, host
, version
, nt_errstr(status
)));
368 talloc_free(tmp_ctx
);
369 cldap_empty_reply(cldap
, message_id
, src
);