1/* 2 Unix SMB/CIFS implementation. 3 4 CLDAP server - netlogon handling 5 6 Copyright (C) Andrew Tridgell 2005 7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008 8 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. 13 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. 18 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/>. 21*/ 22 23#include "includes.h" 24#include "lib/ldb/include/ldb.h" 25#include "lib/ldb/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" 34#include "ldb_wrap.h" 35#include "system/network.h" 36#include "lib/socket/netif.h" 37#include "param/param.h" 38#include "../lib/tsocket/tsocket.h" 39 40/* 41 fill in the cldap netlogon union for a given version 42*/ 43NTSTATUS fill_netlogon_samlogon_response(struct ldb_context *sam_ctx, 44 TALLOC_CTX *mem_ctx, 45 const char *domain, 46 const char *netbios_domain, 47 struct dom_sid *domain_sid, 48 const char *domain_guid, 49 const char *user, 50 uint32_t acct_control, 51 const char *src_address, 52 uint32_t version, 53 struct loadparm_context *lp_ctx, 54 struct netlogon_samlogon_response *netlogon) 55{ 56 const char *dom_attrs[] = {"objectGUID", NULL}; 57 const char *none_attrs[] = {NULL}; 58 struct ldb_result *dom_res = NULL, *user_res = NULL; 59 int ret; 60 const char **services = lp_server_services(lp_ctx); 61 uint32_t server_type; 62 const char *pdc_name; 63 struct GUID domain_uuid; 64 const char *realm; 65 const char *dns_domain; 66 const char *pdc_dns_name; 67 const char *flatname; 68 const char *server_site; 69 const char *client_site; 70 const char *pdc_ip; 71 struct ldb_dn *domain_dn = NULL; 72 struct interface *ifaces; 73 bool user_known; 74 NTSTATUS status; 75 76 /* the domain has an optional trailing . */ 77 if (domain && domain[strlen(domain)-1] == '.') { 78 domain = talloc_strndup(mem_ctx, domain, strlen(domain)-1); 79 } 80 81 if (domain && strcasecmp_m(domain, lp_realm(lp_ctx)) == 0) { 82 domain_dn = ldb_get_default_basedn(sam_ctx); 83 } 84 85 if (netbios_domain && strcasecmp_m(domain, lp_sam_name(lp_ctx))) { 86 domain_dn = ldb_get_default_basedn(sam_ctx); 87 } 88 89 if (domain_dn) { 90 ret = ldb_search(sam_ctx, mem_ctx, &dom_res, 91 domain_dn, LDB_SCOPE_BASE, dom_attrs, 92 "objectClass=domain"); 93 if (ret != LDB_SUCCESS) { 94 DEBUG(2,("Error finding domain '%s'/'%s' in sam: %s\n", domain, ldb_dn_get_linearized(domain_dn), ldb_errstring(sam_ctx))); 95 return NT_STATUS_NO_SUCH_DOMAIN; 96 } 97 if (dom_res->count != 1) { 98 DEBUG(2,("Error finding domain '%s'/'%s' in sam\n", domain, ldb_dn_get_linearized(domain_dn))); 99 return NT_STATUS_NO_SUCH_DOMAIN; 100 } 101 } 102 103 if ((dom_res == NULL || dom_res->count == 0) && (domain_guid || domain_sid)) { 104 105 if (domain_guid) { 106 struct GUID binary_guid; 107 struct ldb_val guid_val; 108 enum ndr_err_code ndr_err; 109 110 /* By this means, we ensure we don't have funny stuff in the GUID */ 111 112 status = GUID_from_string(domain_guid, &binary_guid); 113 if (!NT_STATUS_IS_OK(status)) { 114 return status; 115 } 116 117 /* And this gets the result into the binary format we want anyway */ 118 ndr_err = ndr_push_struct_blob(&guid_val, mem_ctx, NULL, &binary_guid, 119 (ndr_push_flags_fn_t)ndr_push_GUID); 120 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 121 return NT_STATUS_INVALID_PARAMETER; 122 } 123 ret = ldb_search(sam_ctx, mem_ctx, &dom_res, 124 NULL, LDB_SCOPE_SUBTREE, 125 dom_attrs, 126 "(&(objectCategory=DomainDNS)(objectGUID=%s))", 127 ldb_binary_encode(mem_ctx, guid_val)); 128 } else { /* domain_sid case */ 129 struct dom_sid *sid; 130 struct ldb_val sid_val; 131 enum ndr_err_code ndr_err; 132 133 /* Rather than go via the string, just push into the NDR form */ 134 ndr_err = ndr_push_struct_blob(&sid_val, mem_ctx, NULL, &sid, 135 (ndr_push_flags_fn_t)ndr_push_dom_sid); 136 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 137 return NT_STATUS_INVALID_PARAMETER; 138 } 139 140 ret = ldb_search(sam_ctx, mem_ctx, &dom_res, 141 NULL, LDB_SCOPE_SUBTREE, 142 dom_attrs, 143 "(&(objectCategory=DomainDNS)(objectSID=%s))", 144 ldb_binary_encode(mem_ctx, sid_val)); 145 } 146 147 if (ret != LDB_SUCCESS) { 148 DEBUG(2,("Unable to find referece to GUID '%s' or SID %s in sam: %s\n", 149 domain_guid, dom_sid_string(mem_ctx, domain_sid), 150 ldb_errstring(sam_ctx))); 151 return NT_STATUS_NO_SUCH_DOMAIN; 152 } else if (dom_res->count == 1) { 153 /* Ok, now just check it is our domain */ 154 155 if (ldb_dn_compare(ldb_get_default_basedn(sam_ctx), dom_res->msgs[0]->dn) != 0) { 156 return NT_STATUS_NO_SUCH_DOMAIN; 157 } 158 } else if (dom_res->count > 1) { 159 return NT_STATUS_NO_SUCH_DOMAIN; 160 } 161 } 162 163 164 if ((dom_res == NULL || dom_res->count == 0)) { 165 DEBUG(2,("Unable to find domain with name %s or GUID {%s}\n", domain, domain_guid)); 166 return NT_STATUS_NO_SUCH_DOMAIN; 167 } 168 169 /* work around different inputs for not-specified users */ 170 if (!user) { 171 user = ""; 172 } 173 174 /* Enquire about any valid username with just a CLDAP packet - 175 * if kerberos didn't also do this, the security folks would 176 * scream... */ 177 if (user[0]) { \ 178 /* Only allow some bits to be enquired: [MS-ATDS] 7.3.3.2 */ 179 if (acct_control == (uint32_t)-1) { 180 acct_control = 0; 181 } 182 acct_control = acct_control & (ACB_TEMPDUP | ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); 183 184 /* We must exclude disabled accounts, but otherwise do the bitwise match the client asked for */ 185 ret = ldb_search(sam_ctx, mem_ctx, &user_res, 186 dom_res->msgs[0]->dn, LDB_SCOPE_SUBTREE, 187 none_attrs, 188 "(&(objectClass=user)(samAccountName=%s)" 189 "(!(userAccountControl:" LDB_OID_COMPARATOR_AND ":=%u))" 190 "(userAccountControl:" LDB_OID_COMPARATOR_OR ":=%u))", 191 ldb_binary_encode_string(mem_ctx, user), 192 UF_ACCOUNTDISABLE, ds_acb2uf(acct_control)); 193 if (ret != LDB_SUCCESS) { 194 DEBUG(2,("Unable to find referece to user '%s' with ACB 0x%8x under %s: %s\n", 195 user, acct_control, ldb_dn_get_linearized(dom_res->msgs[0]->dn), 196 ldb_errstring(sam_ctx))); 197 return NT_STATUS_NO_SUCH_USER; 198 } else if (user_res->count == 1) { 199 user_known = true; 200 } else { 201 user_known = false; 202 } 203 204 } else { 205 user_known = true; 206 } 207 208 server_type = 209 NBT_SERVER_DS | NBT_SERVER_TIMESERV | 210 NBT_SERVER_CLOSEST | NBT_SERVER_WRITABLE | 211 NBT_SERVER_GOOD_TIMESERV | DS_DNS_CONTROLLER | 212 DS_DNS_DOMAIN; 213 214 if (samdb_is_pdc(sam_ctx)) { 215 int *domainFunctionality; 216 server_type |= NBT_SERVER_PDC; 217 domainFunctionality = talloc_get_type(ldb_get_opaque(sam_ctx, "domainFunctionality"), int); 218 if (domainFunctionality && *domainFunctionality >= DS_DOMAIN_FUNCTION_2008) { 219 server_type |= NBT_SERVER_FULL_SECRET_DOMAIN_6; 220 } 221 } 222 223 if (samdb_is_gc(sam_ctx)) { 224 server_type |= NBT_SERVER_GC; 225 } 226 227 if (str_list_check(services, "ldap")) { 228 server_type |= NBT_SERVER_LDAP; 229 } 230 231 if (str_list_check(services, "kdc")) { 232 server_type |= NBT_SERVER_KDC; 233 } 234 235 if (ldb_dn_compare(ldb_get_root_basedn(sam_ctx), ldb_get_default_basedn(sam_ctx)) == 0) { 236 server_type |= DS_DNS_FOREST; 237 } 238 239 pdc_name = talloc_asprintf(mem_ctx, "\\\\%s", lp_netbios_name(lp_ctx)); 240 domain_uuid = samdb_result_guid(dom_res->msgs[0], "objectGUID"); 241 realm = lp_realm(lp_ctx); 242 dns_domain = lp_realm(lp_ctx); 243 pdc_dns_name = talloc_asprintf(mem_ctx, "%s.%s", 244 strlower_talloc(mem_ctx, 245 lp_netbios_name(lp_ctx)), 246 dns_domain); 247 248 flatname = lp_sam_name(lp_ctx); 249 /* FIXME: Hardcoded site names */ 250 server_site = "Default-First-Site-Name"; 251 client_site = "Default-First-Site-Name"; 252 load_interfaces(mem_ctx, lp_interfaces(lp_ctx), &ifaces); 253 pdc_ip = iface_best_ip(ifaces, src_address); 254 255 ZERO_STRUCTP(netlogon); 256 257 /* check if either of these bits is present */ 258 if (version & (NETLOGON_NT_VERSION_5EX|NETLOGON_NT_VERSION_5EX_WITH_IP)) { 259 uint32_t extra_flags = 0; 260 netlogon->ntver = NETLOGON_NT_VERSION_5EX; 261 262 /* could check if the user exists */ 263 if (user_known) { 264 netlogon->data.nt5_ex.command = LOGON_SAM_LOGON_RESPONSE_EX; 265 } else { 266 netlogon->data.nt5_ex.command = LOGON_SAM_LOGON_USER_UNKNOWN_EX; 267 } 268 netlogon->data.nt5_ex.server_type = server_type; 269 netlogon->data.nt5_ex.domain_uuid = domain_uuid; 270 netlogon->data.nt5_ex.forest = realm; 271 netlogon->data.nt5_ex.dns_domain = dns_domain; 272 netlogon->data.nt5_ex.pdc_dns_name = pdc_dns_name; 273 netlogon->data.nt5_ex.domain = flatname; 274 netlogon->data.nt5_ex.pdc_name = lp_netbios_name(lp_ctx); 275 netlogon->data.nt5_ex.user_name = user; 276 netlogon->data.nt5_ex.server_site = server_site; 277 netlogon->data.nt5_ex.client_site = client_site; 278 279 if (version & NETLOGON_NT_VERSION_5EX_WITH_IP) { 280 /* Clearly this needs to be fixed up for IPv6 */ 281 extra_flags = NETLOGON_NT_VERSION_5EX_WITH_IP; 282 netlogon->data.nt5_ex.sockaddr.sockaddr_family = 2; 283 netlogon->data.nt5_ex.sockaddr.pdc_ip = pdc_ip; 284 netlogon->data.nt5_ex.sockaddr.remaining = data_blob_talloc_zero(mem_ctx, 8); 285 } 286 netlogon->data.nt5_ex.nt_version = NETLOGON_NT_VERSION_1|NETLOGON_NT_VERSION_5EX|extra_flags; 287 netlogon->data.nt5_ex.lmnt_token = 0xFFFF; 288 netlogon->data.nt5_ex.lm20_token = 0xFFFF; 289 290 } else if (version & NETLOGON_NT_VERSION_5) { 291 netlogon->ntver = NETLOGON_NT_VERSION_5; 292 293 /* could check if the user exists */ 294 if (user_known) { 295 netlogon->data.nt5.command = LOGON_SAM_LOGON_RESPONSE; 296 } else { 297 netlogon->data.nt5.command = LOGON_SAM_LOGON_USER_UNKNOWN; 298 } 299 netlogon->data.nt5.pdc_name = pdc_name; 300 netlogon->data.nt5.user_name = user; 301 netlogon->data.nt5.domain_name = flatname; 302 netlogon->data.nt5.domain_uuid = domain_uuid; 303 netlogon->data.nt5.forest = realm; 304 netlogon->data.nt5.dns_domain = dns_domain; 305 netlogon->data.nt5.pdc_dns_name = pdc_dns_name; 306 netlogon->data.nt5.pdc_ip = pdc_ip; 307 netlogon->data.nt5.server_type = server_type; 308 netlogon->data.nt5.nt_version = NETLOGON_NT_VERSION_1|NETLOGON_NT_VERSION_5; 309 netlogon->data.nt5.lmnt_token = 0xFFFF; 310 netlogon->data.nt5.lm20_token = 0xFFFF; 311 312 } else /* (version & NETLOGON_NT_VERSION_1) and all other cases */ { 313 netlogon->ntver = NETLOGON_NT_VERSION_1; 314 /* could check if the user exists */ 315 if (user_known) { 316 netlogon->data.nt4.command = LOGON_SAM_LOGON_RESPONSE; 317 } else { 318 netlogon->data.nt4.command = LOGON_SAM_LOGON_USER_UNKNOWN; 319 } 320 netlogon->data.nt4.server = pdc_name; 321 netlogon->data.nt4.user_name = user; 322 netlogon->data.nt4.domain = flatname; 323 netlogon->data.nt4.nt_version = NETLOGON_NT_VERSION_1; 324 netlogon->data.nt4.lmnt_token = 0xFFFF; 325 netlogon->data.nt4.lm20_token = 0xFFFF; 326 } 327 328 return NT_STATUS_OK; 329} 330 331 332/* 333 handle incoming cldap requests 334*/ 335void cldapd_netlogon_request(struct cldap_socket *cldap, 336 struct cldapd_server *cldapd, 337 TALLOC_CTX *tmp_ctx, 338 uint32_t message_id, 339 struct ldb_parse_tree *tree, 340 struct tsocket_address *src) 341{ 342 int i; 343 const char *domain = NULL; 344 const char *host = NULL; 345 const char *user = NULL; 346 const char *domain_guid = NULL; 347 const char *domain_sid = NULL; 348 int acct_control = -1; 349 int version = -1; 350 struct netlogon_samlogon_response netlogon; 351 NTSTATUS status = NT_STATUS_INVALID_PARAMETER; 352 353 if (tree->operation != LDB_OP_AND) goto failed; 354 355 /* extract the query elements */ 356 for (i=0;i<tree->u.list.num_elements;i++) { 357 struct ldb_parse_tree *t = tree->u.list.elements[i]; 358 if (t->operation != LDB_OP_EQUALITY) goto failed; 359 if (strcasecmp(t->u.equality.attr, "DnsDomain") == 0) { 360 domain = talloc_strndup(tmp_ctx, 361 (const char *)t->u.equality.value.data, 362 t->u.equality.value.length); 363 } 364 if (strcasecmp(t->u.equality.attr, "Host") == 0) { 365 host = talloc_strndup(tmp_ctx, 366 (const char *)t->u.equality.value.data, 367 t->u.equality.value.length); 368 } 369 if (strcasecmp(t->u.equality.attr, "DomainGuid") == 0) { 370 NTSTATUS enc_status; 371 struct GUID guid; 372 enc_status = ldap_decode_ndr_GUID(tmp_ctx, 373 t->u.equality.value, &guid); 374 if (NT_STATUS_IS_OK(enc_status)) { 375 domain_guid = GUID_string(tmp_ctx, &guid); 376 } 377 } 378 if (strcasecmp(t->u.equality.attr, "DomainSid") == 0) { 379 domain_sid = talloc_strndup(tmp_ctx, 380 (const char *)t->u.equality.value.data, 381 t->u.equality.value.length); 382 } 383 if (strcasecmp(t->u.equality.attr, "User") == 0) { 384 user = talloc_strndup(tmp_ctx, 385 (const char *)t->u.equality.value.data, 386 t->u.equality.value.length); 387 } 388 if (strcasecmp(t->u.equality.attr, "NtVer") == 0 && 389 t->u.equality.value.length == 4) { 390 version = IVAL(t->u.equality.value.data, 0); 391 } 392 if (strcasecmp(t->u.equality.attr, "AAC") == 0 && 393 t->u.equality.value.length == 4) { 394 acct_control = IVAL(t->u.equality.value.data, 0); 395 } 396 } 397 398 if (domain_guid == NULL && domain == NULL) { 399 domain = lp_realm(cldapd->task->lp_ctx); 400 } 401 402 if (version == -1) { 403 goto failed; 404 } 405 406 DEBUG(5,("cldap netlogon query domain=%s host=%s user=%s version=%d guid=%s\n", 407 domain, host, user, version, domain_guid)); 408 409 status = fill_netlogon_samlogon_response(cldapd->samctx, tmp_ctx, domain, NULL, NULL, domain_guid, 410 user, acct_control, 411 tsocket_address_inet_addr_string(src, tmp_ctx), 412 version, cldapd->task->lp_ctx, &netlogon); 413 if (!NT_STATUS_IS_OK(status)) { 414 goto failed; 415 } 416 417 status = cldap_netlogon_reply(cldap, 418 lp_iconv_convenience(cldapd->task->lp_ctx), 419 message_id, src, version, 420 &netlogon); 421 if (!NT_STATUS_IS_OK(status)) { 422 goto failed; 423 } 424 425 return; 426 427failed: 428 DEBUG(2,("cldap netlogon query failed domain=%s host=%s version=%d - %s\n", 429 domain, host, version, nt_errstr(status))); 430 cldap_empty_reply(cldap, message_id, src); 431} 432