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