1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * This module handles the primary domain controller location protocol. 30 * The document claims to be version 1.15 of the browsing protocol. It also 31 * claims to specify the mailslot protocol. 32 * 33 * The NETLOGON protocol uses \MAILSLOT\NET mailslots. The protocol 34 * specification is incomplete, contains errors and is out-of-date but 35 * it does provide some useful background information. The document 36 * doesn't mention the NETLOGON_SAMLOGON version of the protocol. 37 */ 38 39 #include <stdlib.h> 40 #include <syslog.h> 41 #include <alloca.h> 42 #include <arpa/inet.h> 43 #include <resolv.h> 44 45 #include <smbsrv/mailslot.h> 46 #include <smbsrv/libsmbns.h> 47 #include <smbns_ads.h> 48 #include <smbns_browser.h> 49 #include <smbns_netbios.h> 50 51 static void smb_netlogon_query(struct name_entry *server, char *mailbox, 52 char *domain); 53 54 static void smb_netlogon_samlogon(struct name_entry *server, char *mailbox, 55 char *domain); 56 57 static void smb_netlogon_send(struct name_entry *name, char *domain, 58 unsigned char *buffer, int count); 59 60 static void smb_netlogon_rdc_rsp(char *src_name, uint32_t src_ipaddr); 61 static int better_dc(uint32_t cur_ip, uint32_t new_ip); 62 63 static char resource_domain[SMB_PI_MAX_DOMAIN]; 64 65 /* 66 * smb_netlogon_request 67 * 68 * This is the entry point locating the resource domain PDC. A netlogon 69 * request is sent using the specified protocol on the specified network. 70 * Note that we need to know the domain SID in order to use the samlogon 71 * format. 72 * 73 * Netlogon responses are received asynchronously and eventually handled 74 * in smb_netlogon_receive. 75 */ 76 void 77 smb_netlogon_request(int net, int protocol, char *domain) 78 { 79 struct name_entry *server; 80 nt_domain_t *ntdp; 81 82 server = smb_browser_get_srvname(net); 83 if (server == 0) 84 return; 85 86 (void) strlcpy(resource_domain, domain, 87 sizeof (resource_domain)); 88 89 if (strlen(resource_domain) > 0) { 90 ntdp = nt_domain_lookup_name(resource_domain); 91 if (protocol == NETLOGON_PROTO_SAMLOGON && ntdp) 92 smb_netlogon_samlogon(server, 93 MAILSLOT_NETLOGON_SAMLOGON_RDC, 94 resource_domain); 95 else 96 smb_netlogon_query(server, 97 MAILSLOT_NETLOGON_RDC, 98 resource_domain); 99 } 100 } 101 102 /* 103 * smb_netlogon_receive 104 * 105 * This is where we handle all incoming NetLogon messages. Currently, we 106 * ignore requests from anyone else. We are only interested in responses 107 * to our own requests. The NetLogonResponse provides the name of the PDC. 108 * If we don't already have a controller name, we use the name provided 109 * in the message. Otherwise we use the name already in the environment. 110 */ 111 void 112 smb_netlogon_receive(struct datagram *datagram, 113 char *mailbox, 114 unsigned char *data, 115 int datalen) 116 { 117 struct netlogon_opt { 118 char *mailslot; 119 void (*handler)(); 120 } netlogon_opt[] = { 121 { MAILSLOT_NETLOGON_RDC, smb_netlogon_rdc_rsp }, 122 { MAILSLOT_NETLOGON_SAMLOGON_RDC, smb_netlogon_rdc_rsp }, 123 }; 124 125 smb_msgbuf_t mb; 126 unsigned short opcode; 127 char src_name[SMB_PI_MAX_HOST]; 128 mts_wchar_t unicode_src_name[SMB_PI_MAX_HOST]; 129 unsigned int cpid = oem_get_smb_cpid(); 130 uint32_t src_ipaddr; 131 char *junk; 132 char *primary; 133 char *domain; 134 int i; 135 char ipstr[16]; 136 int rc; 137 138 src_ipaddr = datagram->src.addr_list.sin.sin_addr.s_addr; 139 140 /* 141 * The datagram->src.name is in oem codepage format. 142 * Therefore, we need to convert it to unicode and 143 * store it in multi-bytes format. 144 */ 145 (void) oemstounicodes(unicode_src_name, (char *)datagram->src.name, 146 SMB_PI_MAX_HOST, cpid); 147 (void) mts_wcstombs(src_name, unicode_src_name, SMB_PI_MAX_HOST); 148 149 (void) trim_whitespace(src_name); 150 151 (void) inet_ntop(AF_INET, (const void *)(&src_ipaddr), ipstr, 152 sizeof (ipstr)); 153 syslog(LOG_DEBUG, "NetLogonReceive: src=%s [%s], mbx=%s", 154 src_name, ipstr, mailbox); 155 156 smb_msgbuf_init(&mb, data, datalen, 0); 157 158 if (smb_msgbuf_decode(&mb, "w", &opcode) < 0) { 159 syslog(LOG_ERR, "NetLogonReceive: decode error"); 160 smb_msgbuf_term(&mb); 161 return; 162 } 163 164 switch (opcode) { 165 case LOGON_PRIMARY_RESPONSE: 166 /* 167 * Message contains: 168 * PDC name (MBS), PDC name (Unicode), Domain name (unicode) 169 */ 170 rc = smb_msgbuf_decode(&mb, "sUU", &junk, &primary, &domain); 171 if (rc < 0) { 172 syslog(LOG_ERR, 173 "NetLogonResponse: opcode %d decode error", 174 opcode); 175 smb_msgbuf_term(&mb); 176 return; 177 } 178 break; 179 180 case LOGON_SAM_LOGON_RESPONSE: 181 case LOGON_SAM_USER_UNKNOWN: 182 /* 183 * Message contains: 184 * PDC name, User name, Domain name (all unicode) 185 */ 186 rc = smb_msgbuf_decode(&mb, "UUU", &primary, &junk, &domain); 187 if (rc < 0) { 188 syslog(LOG_ERR, 189 "NetLogonResponse: opcode %d decode error", 190 opcode); 191 smb_msgbuf_term(&mb); 192 return; 193 } 194 195 /* 196 * skip past the "\\" prefix 197 */ 198 primary += strspn(primary, "\\"); 199 break; 200 201 default: 202 /* 203 * We don't respond to PDC discovery requests. 204 */ 205 syslog(LOG_DEBUG, "NetLogonReceive: opcode 0x%04x", opcode); 206 smb_msgbuf_term(&mb); 207 return; 208 } 209 210 if (domain == 0 || primary == 0) { 211 syslog(LOG_ERR, "NetLogonResponse: malformed packet"); 212 smb_msgbuf_term(&mb); 213 return; 214 } 215 216 syslog(LOG_DEBUG, "DC Offer Dom=%s PDC=%s From=%s", 217 domain, primary, src_name); 218 219 if (strcasecmp(domain, resource_domain)) { 220 syslog(LOG_DEBUG, "NetLogonResponse: other domain " 221 "%s, requested %s", domain, resource_domain); 222 smb_msgbuf_term(&mb); 223 return; 224 } 225 226 for (i = 0; i < sizeof (netlogon_opt)/sizeof (netlogon_opt[0]); ++i) { 227 if (strcasecmp(netlogon_opt[i].mailslot, mailbox) == 0) { 228 syslog(LOG_DEBUG, "NetLogonReceive: %s", mailbox); 229 (*netlogon_opt[i].handler)(primary, src_ipaddr); 230 smb_msgbuf_term(&mb); 231 return; 232 } 233 } 234 235 syslog(LOG_DEBUG, "NetLogonReceive[%s]: unknown mailslot", mailbox); 236 smb_msgbuf_term(&mb); 237 } 238 239 240 241 /* 242 * smb_netlogon_query 243 * 244 * Build and send a LOGON_PRIMARY_QUERY to the MAILSLOT_NETLOGON. At some 245 * point we should receive a LOGON_PRIMARY_RESPONSE in the mailslot we 246 * specify in the request. 247 * 248 * struct NETLOGON_QUERY { 249 * unsigned short Opcode; # LOGON_PRIMARY_QUERY 250 * char ComputerName[]; # ASCII hostname. The response 251 * # is sent to <ComputerName>(00). 252 * char MailslotName[]; # MAILSLOT_NETLOGON 253 * char Pad[]; # Pad to short 254 * wchar_t ComputerName[] # UNICODE hostname 255 * DWORD NT_Version; # 0x00000001 256 * WORD LmNTToken; # 0xffff 257 * WORD Lm20Token; # 0xffff 258 * }; 259 */ 260 static void 261 smb_netlogon_query(struct name_entry *server, 262 char *mailbox, 263 char *domain) 264 { 265 smb_msgbuf_t mb; 266 int offset, announce_len, data_length, name_lengths; 267 unsigned char buffer[MAX_DATAGRAM_LENGTH]; 268 char hostname[MAXHOSTNAMELEN]; 269 270 if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0) 271 return; 272 273 name_lengths = strlen(mailbox)+1+strlen(hostname)+1; 274 275 /* 276 * The (name_lengths & 1) part is to word align the name_lengths 277 * before the wc equiv strlen and the "+ 2" is to cover the two 278 * zero bytes that terminate the wchar string. 279 */ 280 data_length = sizeof (short) + name_lengths + (name_lengths & 1) + 281 mts_wcequiv_strlen(hostname) + 2 + sizeof (long) + sizeof (short) + 282 sizeof (short); 283 284 offset = smb_browser_load_transact_header(buffer, 285 sizeof (buffer), data_length, ONE_WAY_TRANSACTION, 286 MAILSLOT_NETLOGON); 287 288 if (offset < 0) 289 return; 290 291 smb_msgbuf_init(&mb, buffer + offset, sizeof (buffer) - offset, 0); 292 293 announce_len = smb_msgbuf_encode(&mb, "wssUlww", 294 (short)LOGON_PRIMARY_QUERY, 295 hostname, 296 mailbox, 297 hostname, 298 0x1, 299 0xffff, 300 0xffff); 301 302 if (announce_len <= 0) { 303 smb_msgbuf_term(&mb); 304 syslog(LOG_ERR, "NetLogonQuery: encode error"); 305 return; 306 } 307 308 smb_netlogon_send(server, domain, buffer, offset + announce_len); 309 smb_msgbuf_term(&mb); 310 } 311 312 313 /* 314 * smb_netlogon_samlogon 315 * 316 * The SamLogon version of the NetLogon request uses the workstation trust 317 * account and, I think, may be a prerequisite to the challenge/response 318 * netr authentication. The trust account username is the hostname with a 319 * $ appended. The mailslot for this request is MAILSLOT_NTLOGON. At some 320 * we should receive a LOGON_SAM_LOGON_RESPONSE in the mailslot we 321 * specify in the request. 322 * 323 * struct NETLOGON_SAM_LOGON { 324 * unsigned short Opcode; # LOGON_SAM_LOGON_REQUEST 325 * unsigned short RequestCount; # 0 326 * wchar_t UnicodeComputerName; # hostname 327 * wchar_t UnicodeUserName; # hostname$ 328 * char *MailslotName; # response mailslot 329 * DWORD AllowableAccountControlBits; # 0x80 = WorkstationTrustAccount 330 * DWORD DomainSidSize; # domain sid length in bytes 331 * BYTE *DomainSid; # domain sid 332 * uint32_t NT_Version; # 0x00000001 333 * unsigned short LmNTToken; # 0xffff 334 * unsigned short Lm20Token; # 0xffff 335 * }; 336 */ 337 static void 338 smb_netlogon_samlogon(struct name_entry *server, 339 char *mailbox, 340 char *domain) 341 { 342 smb_msgbuf_t mb; 343 nt_domain_t *ntdp; 344 nt_sid_t *domain_sid; 345 unsigned domain_sid_len; 346 char *username; 347 unsigned char buffer[MAX_DATAGRAM_LENGTH]; 348 int offset; 349 int announce_len; 350 int data_length; 351 int name_length; 352 char hostname[MAXHOSTNAMELEN]; 353 354 syslog(LOG_DEBUG, "NetLogonSamLogonReq: %s", domain); 355 356 if ((ntdp = nt_domain_lookup_name(domain)) == 0) { 357 syslog(LOG_ERR, "NetLogonSamLogonReq[%s]: no sid", domain); 358 return; 359 } 360 361 domain_sid = ntdp->sid; 362 domain_sid_len = nt_sid_length(domain_sid); 363 nt_sid_logf(domain_sid); 364 365 if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0) 366 return; 367 368 /* 369 * The username will be the trust account name on the PDC. 370 */ 371 name_length = strlen(hostname) + 2; 372 username = alloca(name_length); 373 (void) snprintf(username, name_length, "%s$", hostname); 374 375 /* 376 * Add 2 to wide-char equivalent strlen to cover the 377 * two zero bytes that terminate the wchar string. 378 */ 379 name_length = strlen(mailbox)+1; 380 381 data_length = sizeof (short) 382 + sizeof (short) 383 + mts_wcequiv_strlen(hostname) + 2 384 + mts_wcequiv_strlen(username) + 2 385 + name_length 386 + sizeof (long) 387 + sizeof (long) 388 + domain_sid_len + 3 /* padding */ 389 + sizeof (long) 390 + sizeof (short) 391 + sizeof (short); 392 393 offset = smb_browser_load_transact_header(buffer, 394 sizeof (buffer), data_length, ONE_WAY_TRANSACTION, 395 MAILSLOT_NTLOGON); 396 397 if (offset < 0) { 398 syslog(LOG_ERR, "NetLogonSamLogonReq: header error"); 399 return; 400 } 401 402 /* 403 * The domain SID is padded with 3 leading zeros. 404 */ 405 smb_msgbuf_init(&mb, buffer + offset, sizeof (buffer) - offset, 0); 406 announce_len = smb_msgbuf_encode(&mb, "wwUUsll3.#clww", 407 (short)LOGON_SAM_LOGON_REQUEST, 408 0, /* RequestCount */ 409 hostname, /* UnicodeComputerName */ 410 username, /* UnicodeUserName */ 411 mailbox, /* MailslotName */ 412 0x00000080, /* AllowableAccountControlBits */ 413 domain_sid_len, /* DomainSidSize */ 414 domain_sid_len, domain_sid, /* DomainSid */ 415 0x00000001, /* NT_Version */ 416 0xffff, /* LmNTToken */ 417 0xffff); /* Lm20Token */ 418 419 if (announce_len <= 0) { 420 syslog(LOG_ERR, "NetLogonSamLogonReq: encode error"); 421 smb_msgbuf_term(&mb); 422 return; 423 } 424 425 smb_netlogon_send(server, domain, buffer, offset + announce_len); 426 smb_msgbuf_term(&mb); 427 } 428 429 430 /* 431 * Send a query for each version of the protocol. 432 */ 433 static void 434 smb_netlogon_send(struct name_entry *name, 435 char *domain, 436 unsigned char *buffer, 437 int count) 438 { 439 static char suffix[] = { 0x1B, 0x1C }; 440 struct name_entry dname; 441 struct name_entry *dest; 442 struct name_entry *dest_dup; 443 int i; 444 445 for (i = 0; i < sizeof (suffix)/sizeof (suffix[0]); i++) { 446 smb_init_name_struct((unsigned char *)domain, suffix[i], 447 0, 0, 0, 0, 0, &dname); 448 449 syslog(LOG_DEBUG, "smb_netlogon_send"); 450 smb_netbios_name_dump(&dname); 451 if ((dest = smb_name_find_name(&dname)) != 0) { 452 dest_dup = smb_netbios_name_dup(dest, 1); 453 smb_name_unlock_name(dest); 454 if (dest_dup) { 455 (void) smb_netbios_datagram_send(name, dest_dup, 456 buffer, count); 457 free(dest_dup); 458 } 459 } else { 460 syslog(LOG_DEBUG, "smbd: NBNS couldn't find %s<0x%X>", 461 domain, suffix[i]); 462 } 463 } 464 } 465 466 /* 467 * smb_netlogon_rdc_rsp 468 * 469 * This is where we process netlogon responses for the resource domain. 470 * The src_name is the real name of the remote machine. 471 */ 472 static void 473 smb_netlogon_rdc_rsp(char *src_name, uint32_t src_ipaddr) 474 { 475 static int initialized = 0; 476 smb_ntdomain_t *pi; 477 uint32_t ipaddr; 478 uint32_t prefer_ipaddr = 0; 479 char ipstr[16]; 480 char srcip[16]; 481 int rc; 482 483 (void) inet_ntop(AF_INET, (const void *)(&src_ipaddr), 484 srcip, sizeof (srcip)); 485 486 rc = smb_config_getstr(SMB_CI_DOMAIN_SRV, ipstr, sizeof (ipstr)); 487 if (rc == SMBD_SMF_OK) { 488 rc = inet_pton(AF_INET, ipstr, &prefer_ipaddr); 489 if (rc == 0) 490 prefer_ipaddr = 0; 491 492 if (!initialized) { 493 syslog(LOG_DEBUG, "SMB DC Preference: %s", ipstr); 494 initialized = 1; 495 } 496 } 497 498 syslog(LOG_DEBUG, "DC Offer [%s]: %s [%s]", 499 resource_domain, src_name, srcip); 500 501 if ((pi = smb_getdomaininfo(0)) != 0) { 502 if (prefer_ipaddr != 0 && prefer_ipaddr == pi->ipaddr) { 503 syslog(LOG_DEBUG, "DC for %s: %s [%s]", 504 resource_domain, src_name, srcip); 505 return; 506 } 507 508 ipaddr = pi->ipaddr; 509 } else 510 ipaddr = 0; 511 512 if (better_dc(ipaddr, src_ipaddr) || 513 (prefer_ipaddr != 0 && prefer_ipaddr == src_ipaddr)) { 514 smb_setdomaininfo(resource_domain, src_name, 515 src_ipaddr); 516 syslog(LOG_DEBUG, "DC discovered for %s: %s [%s]", 517 resource_domain, src_name, srcip); 518 } 519 } 520 521 static int 522 better_dc(uint32_t cur_ip, uint32_t new_ip) 523 { 524 net_cfg_t cfg; 525 526 /* 527 * If we don't have any current DC, 528 * then use the new one of course. 529 */ 530 if (cur_ip == 0) 531 return (1); 532 533 if (smb_nic_get_bysubnet(cur_ip, &cfg) != NULL) 534 return (0); 535 if (smb_nic_get_bysubnet(new_ip, &cfg) != NULL) 536 return (1); 537 /* 538 * Otherwise, just keep the old one. 539 */ 540 return (0); 541 } 542 543 /* 544 * msdcs_lookup_ads 545 * 546 * Try to find a domain controller in ADS. Actually we want to query DNS 547 * but we need to find out if ADS is enabled and this is probably the 548 * best way. The IP address isn't set up in the ADS_HANDLE so we need to 549 * make the ads_find_host call. This will only succeed if ADS is enabled. 550 * 551 * Parameter: 552 * nbt_domain - NETBIOS name of the domain 553 * 554 * Returns 1 if a domain controller was found and its name and IP address 555 * have been updated. Otherwise returns 0. 556 */ 557 int 558 msdcs_lookup_ads(char *nbt_domain) 559 { 560 ADS_HOST_INFO *hinfo = 0; 561 int ads_port = 0; 562 char ads_domain[MAXHOSTNAMELEN]; 563 char site_service[MAXHOSTNAMELEN]; 564 char service[MAXHOSTNAMELEN]; 565 char site[MAXHOSTNAMELEN]; 566 char *p; 567 char *ip_addr; 568 struct in_addr ns_list[MAXNS]; 569 int i, cnt, go_next; 570 571 if (!nbt_domain) 572 return (0); 573 574 (void) strlcpy(resource_domain, nbt_domain, SMB_PI_MAX_DOMAIN); 575 if (smb_resolve_fqdn(nbt_domain, ads_domain, MAXHOSTNAMELEN) != 1) 576 return (0); 577 578 (void) smb_config_getstr(SMB_CI_ADS_SITE, site, sizeof (site)); 579 if (*site != '\0') { 580 (void) snprintf(site_service, MAXHOSTNAMELEN, 581 "_ldap._tcp.%s._sites.dc._msdcs.%s", 582 site, ads_domain); 583 } 584 585 (void) snprintf(service, MAXHOSTNAMELEN, 586 "_ldap._tcp.dc._msdcs.%s", ads_domain); 587 588 cnt = smb_get_nameservers(ns_list, MAXNS); 589 590 go_next = 0; 591 for (i = 0; i < cnt; i++) { 592 ip_addr = inet_ntoa(ns_list[i]); 593 594 hinfo = ads_find_host(ip_addr, ads_domain, &ads_port, 595 site_service, &go_next); 596 597 if (hinfo == NULL) { 598 hinfo = ads_find_host(ip_addr, ads_domain, &ads_port, 599 service, &go_next); 600 } 601 602 if ((hinfo != NULL) || (go_next == 0)) 603 break; 604 } 605 606 if (hinfo == NULL) { 607 syslog(LOG_DEBUG, "msdcsLookupADS: unable to find host"); 608 return (0); 609 } 610 611 syslog(LOG_DEBUG, "msdcsLookupADS: %s [%I]", hinfo->name, 612 hinfo->ip_addr); 613 614 /* 615 * Remove the domain extension - the 616 * NetBIOS browser can't handle it. 617 */ 618 if ((p = strchr(hinfo->name, '.')) != 0) 619 *p = '\0'; 620 621 smb_netlogon_rdc_rsp(hinfo->name, hinfo->ip_addr); 622 623 return (1); 624 } 625