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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "@(#)smbns_netlogon.c 1.7 08/07/16 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 smb_better_dc(uint32_t cur_ip, uint32_t new_ip); 62 63 /* 64 * ntdomain_info 65 * Temporary. It should be removed once NBTD is integrated. 66 */ 67 extern smb_ntdomain_t ntdomain_info; 68 extern mutex_t ntdomain_mtx; 69 extern cond_t ntdomain_cv; 70 71 /* 72 * smb_netlogon_request 73 * 74 * This is the entry point locating the resource domain PDC. A netlogon 75 * request is sent using the specified protocol on the specified network. 76 * Note that we need to know the domain SID in order to use the samlogon 77 * format. 78 * 79 * Netlogon responses are received asynchronously and eventually handled 80 * in smb_netlogon_receive. 81 */ 82 void 83 smb_netlogon_request(struct name_entry *server, int protocol, char *domain) 84 { 85 nt_domain_t *ntdp; 86 87 if (domain == NULL || *domain == '\0') 88 return; 89 90 (void) mutex_lock(&ntdomain_mtx); 91 (void) strlcpy(ntdomain_info.n_domain, domain, 92 sizeof (ntdomain_info.n_domain)); 93 (void) mutex_unlock(&ntdomain_mtx); 94 95 ntdp = nt_domain_lookup_name(domain); 96 if (ntdp && (protocol == NETLOGON_PROTO_SAMLOGON)) 97 smb_netlogon_samlogon(server, 98 MAILSLOT_NETLOGON_SAMLOGON_RDC, 99 domain); 100 else 101 smb_netlogon_query(server, 102 MAILSLOT_NETLOGON_RDC, 103 domain); 104 } 105 106 /* 107 * smb_netlogon_receive 108 * 109 * This is where we handle all incoming NetLogon messages. Currently, we 110 * ignore requests from anyone else. We are only interested in responses 111 * to our own requests. The NetLogonResponse provides the name of the PDC. 112 * If we don't already have a controller name, we use the name provided 113 * in the message. Otherwise we use the name already in the environment. 114 */ 115 void 116 smb_netlogon_receive(struct datagram *datagram, 117 char *mailbox, 118 unsigned char *data, 119 int datalen) 120 { 121 struct netlogon_opt { 122 char *mailslot; 123 void (*handler)(); 124 } netlogon_opt[] = { 125 { MAILSLOT_NETLOGON_RDC, smb_netlogon_rdc_rsp }, 126 { MAILSLOT_NETLOGON_SAMLOGON_RDC, smb_netlogon_rdc_rsp }, 127 }; 128 129 smb_msgbuf_t mb; 130 unsigned short opcode; 131 char src_name[SMB_PI_MAX_HOST]; 132 mts_wchar_t unicode_src_name[SMB_PI_MAX_HOST]; 133 unsigned int cpid = oem_get_smb_cpid(); 134 uint32_t src_ipaddr; 135 char *junk; 136 char *primary; 137 char *domain; 138 int i; 139 char ipstr[16]; 140 int rc; 141 142 src_ipaddr = datagram->src.addr_list.sin.sin_addr.s_addr; 143 144 /* 145 * The datagram->src.name is in oem codepage format. 146 * Therefore, we need to convert it to unicode and 147 * store it in multi-bytes format. 148 */ 149 (void) oemstounicodes(unicode_src_name, (char *)datagram->src.name, 150 SMB_PI_MAX_HOST, cpid); 151 (void) mts_wcstombs(src_name, unicode_src_name, SMB_PI_MAX_HOST); 152 153 (void) trim_whitespace(src_name); 154 155 (void) inet_ntop(AF_INET, (const void *)(&src_ipaddr), ipstr, 156 sizeof (ipstr)); 157 syslog(LOG_DEBUG, "NetLogonReceive: src=%s [%s], mbx=%s", 158 src_name, ipstr, mailbox); 159 160 smb_msgbuf_init(&mb, data, datalen, 0); 161 162 if (smb_msgbuf_decode(&mb, "w", &opcode) < 0) { 163 syslog(LOG_ERR, "NetLogonReceive: decode error"); 164 smb_msgbuf_term(&mb); 165 return; 166 } 167 168 switch (opcode) { 169 case LOGON_PRIMARY_RESPONSE: 170 /* 171 * Message contains: 172 * PDC name (MBS), PDC name (Unicode), Domain name (unicode) 173 */ 174 rc = smb_msgbuf_decode(&mb, "sUU", &junk, &primary, &domain); 175 if (rc < 0) { 176 syslog(LOG_ERR, 177 "NetLogonResponse: opcode %d decode error", 178 opcode); 179 smb_msgbuf_term(&mb); 180 return; 181 } 182 break; 183 184 case LOGON_SAM_LOGON_RESPONSE: 185 case LOGON_SAM_USER_UNKNOWN: 186 /* 187 * Message contains: 188 * PDC name, User name, Domain name (all unicode) 189 */ 190 rc = smb_msgbuf_decode(&mb, "UUU", &primary, &junk, &domain); 191 if (rc < 0) { 192 syslog(LOG_ERR, 193 "NetLogonResponse: opcode %d decode error", 194 opcode); 195 smb_msgbuf_term(&mb); 196 return; 197 } 198 199 /* 200 * skip past the "\\" prefix 201 */ 202 primary += strspn(primary, "\\"); 203 break; 204 205 default: 206 /* 207 * We don't respond to PDC discovery requests. 208 */ 209 syslog(LOG_DEBUG, "NetLogonReceive: opcode 0x%04x", opcode); 210 smb_msgbuf_term(&mb); 211 return; 212 } 213 214 if (domain == 0 || primary == 0) { 215 syslog(LOG_ERR, "NetLogonResponse: malformed packet"); 216 smb_msgbuf_term(&mb); 217 return; 218 } 219 220 syslog(LOG_DEBUG, "DC Offer Dom=%s PDC=%s From=%s", 221 domain, primary, src_name); 222 223 (void) mutex_lock(&ntdomain_mtx); 224 if (strcasecmp(domain, ntdomain_info.n_domain)) { 225 syslog(LOG_DEBUG, "NetLogonResponse: other domain " 226 "%s, requested %s", domain, ntdomain_info.n_domain); 227 smb_msgbuf_term(&mb); 228 (void) mutex_unlock(&ntdomain_mtx); 229 return; 230 } 231 (void) mutex_unlock(&ntdomain_mtx); 232 233 for (i = 0; i < sizeof (netlogon_opt)/sizeof (netlogon_opt[0]); ++i) { 234 if (strcasecmp(netlogon_opt[i].mailslot, mailbox) == 0) { 235 syslog(LOG_DEBUG, "NetLogonReceive: %s", mailbox); 236 (*netlogon_opt[i].handler)(primary, src_ipaddr); 237 smb_msgbuf_term(&mb); 238 return; 239 } 240 } 241 242 syslog(LOG_DEBUG, "NetLogonReceive[%s]: unknown mailslot", mailbox); 243 smb_msgbuf_term(&mb); 244 } 245 246 247 248 /* 249 * smb_netlogon_query 250 * 251 * Build and send a LOGON_PRIMARY_QUERY to the MAILSLOT_NETLOGON. At some 252 * point we should receive a LOGON_PRIMARY_RESPONSE in the mailslot we 253 * specify in the request. 254 * 255 * struct NETLOGON_QUERY { 256 * unsigned short Opcode; # LOGON_PRIMARY_QUERY 257 * char ComputerName[]; # ASCII hostname. The response 258 * # is sent to <ComputerName>(00). 259 * char MailslotName[]; # MAILSLOT_NETLOGON 260 * char Pad[]; # Pad to short 261 * wchar_t ComputerName[] # UNICODE hostname 262 * DWORD NT_Version; # 0x00000001 263 * WORD LmNTToken; # 0xffff 264 * WORD Lm20Token; # 0xffff 265 * }; 266 */ 267 static void 268 smb_netlogon_query(struct name_entry *server, 269 char *mailbox, 270 char *domain) 271 { 272 smb_msgbuf_t mb; 273 int offset, announce_len, data_length, name_lengths; 274 unsigned char buffer[MAX_DATAGRAM_LENGTH]; 275 char hostname[NETBIOS_NAME_SZ]; 276 277 if (smb_getnetbiosname(hostname, sizeof (hostname)) != 0) 278 return; 279 280 name_lengths = strlen(mailbox)+1+strlen(hostname)+1; 281 282 /* 283 * The (name_lengths & 1) part is to word align the name_lengths 284 * before the wc equiv strlen and the "+ 2" is to cover the two 285 * zero bytes that terminate the wchar string. 286 */ 287 data_length = sizeof (short) + name_lengths + (name_lengths & 1) + 288 mts_wcequiv_strlen(hostname) + 2 + sizeof (long) + sizeof (short) + 289 sizeof (short); 290 291 offset = smb_browser_load_transact_header(buffer, 292 sizeof (buffer), data_length, ONE_WAY_TRANSACTION, 293 MAILSLOT_NETLOGON); 294 295 if (offset < 0) 296 return; 297 298 smb_msgbuf_init(&mb, buffer + offset, sizeof (buffer) - offset, 0); 299 300 announce_len = smb_msgbuf_encode(&mb, "wssUlww", 301 (short)LOGON_PRIMARY_QUERY, 302 hostname, 303 mailbox, 304 hostname, 305 0x1, 306 0xffff, 307 0xffff); 308 309 if (announce_len <= 0) { 310 smb_msgbuf_term(&mb); 311 syslog(LOG_ERR, "NetLogonQuery: encode error"); 312 return; 313 } 314 315 smb_netlogon_send(server, domain, buffer, offset + announce_len); 316 smb_msgbuf_term(&mb); 317 } 318 319 320 /* 321 * smb_netlogon_samlogon 322 * 323 * The SamLogon version of the NetLogon request uses the workstation trust 324 * account and, I think, may be a prerequisite to the challenge/response 325 * netr authentication. The trust account username is the hostname with a 326 * $ appended. The mailslot for this request is MAILSLOT_NTLOGON. At some 327 * we should receive a LOGON_SAM_LOGON_RESPONSE in the mailslot we 328 * specify in the request. 329 * 330 * struct NETLOGON_SAM_LOGON { 331 * unsigned short Opcode; # LOGON_SAM_LOGON_REQUEST 332 * unsigned short RequestCount; # 0 333 * wchar_t UnicodeComputerName; # hostname 334 * wchar_t UnicodeUserName; # hostname$ 335 * char *MailslotName; # response mailslot 336 * DWORD AllowableAccountControlBits; # 0x80 = WorkstationTrustAccount 337 * DWORD DomainSidSize; # domain sid length in bytes 338 * BYTE *DomainSid; # domain sid 339 * uint32_t NT_Version; # 0x00000001 340 * unsigned short LmNTToken; # 0xffff 341 * unsigned short Lm20Token; # 0xffff 342 * }; 343 */ 344 static void 345 smb_netlogon_samlogon(struct name_entry *server, 346 char *mailbox, 347 char *domain) 348 { 349 smb_msgbuf_t mb; 350 nt_domain_t *ntdp; 351 smb_sid_t *domain_sid; 352 unsigned domain_sid_len; 353 char *username; 354 unsigned char buffer[MAX_DATAGRAM_LENGTH]; 355 int offset; 356 int announce_len; 357 int data_length; 358 int name_length; 359 char hostname[NETBIOS_NAME_SZ]; 360 361 syslog(LOG_DEBUG, "NetLogonSamLogonReq: %s", domain); 362 363 if ((ntdp = nt_domain_lookup_name(domain)) == 0) { 364 syslog(LOG_ERR, "NetLogonSamLogonReq[%s]: no sid", domain); 365 return; 366 } 367 368 domain_sid = ntdp->sid; 369 domain_sid_len = smb_sid_len(domain_sid); 370 371 if (smb_getnetbiosname(hostname, sizeof (hostname)) != 0) 372 return; 373 374 /* 375 * The username will be the trust account name on the PDC. 376 */ 377 name_length = strlen(hostname) + 2; 378 username = alloca(name_length); 379 (void) snprintf(username, name_length, "%s$", hostname); 380 381 /* 382 * Add 2 to wide-char equivalent strlen to cover the 383 * two zero bytes that terminate the wchar string. 384 */ 385 name_length = strlen(mailbox)+1; 386 387 data_length = sizeof (short) 388 + sizeof (short) 389 + mts_wcequiv_strlen(hostname) + 2 390 + mts_wcequiv_strlen(username) + 2 391 + name_length 392 + sizeof (long) 393 + sizeof (long) 394 + domain_sid_len + 3 /* padding */ 395 + sizeof (long) 396 + sizeof (short) 397 + sizeof (short); 398 399 offset = smb_browser_load_transact_header(buffer, 400 sizeof (buffer), data_length, ONE_WAY_TRANSACTION, 401 MAILSLOT_NTLOGON); 402 403 if (offset < 0) { 404 syslog(LOG_ERR, "NetLogonSamLogonReq: header error"); 405 return; 406 } 407 408 /* 409 * The domain SID is padded with 3 leading zeros. 410 */ 411 smb_msgbuf_init(&mb, buffer + offset, sizeof (buffer) - offset, 0); 412 announce_len = smb_msgbuf_encode(&mb, "wwUUsll3.#clww", 413 (short)LOGON_SAM_LOGON_REQUEST, 414 0, /* RequestCount */ 415 hostname, /* UnicodeComputerName */ 416 username, /* UnicodeUserName */ 417 mailbox, /* MailslotName */ 418 0x00000080, /* AllowableAccountControlBits */ 419 domain_sid_len, /* DomainSidSize */ 420 domain_sid_len, domain_sid, /* DomainSid */ 421 0x00000001, /* NT_Version */ 422 0xffff, /* LmNTToken */ 423 0xffff); /* Lm20Token */ 424 425 if (announce_len <= 0) { 426 syslog(LOG_ERR, "NetLogonSamLogonReq: encode error"); 427 smb_msgbuf_term(&mb); 428 return; 429 } 430 431 smb_netlogon_send(server, domain, buffer, offset + announce_len); 432 smb_msgbuf_term(&mb); 433 } 434 435 436 /* 437 * Send a query for each version of the protocol. 438 */ 439 static void 440 smb_netlogon_send(struct name_entry *name, 441 char *domain, 442 unsigned char *buffer, 443 int count) 444 { 445 static char suffix[] = { 0x1B, 0x1C }; 446 struct name_entry dname; 447 struct name_entry *dest; 448 struct name_entry *dest_dup; 449 int i; 450 451 for (i = 0; i < sizeof (suffix)/sizeof (suffix[0]); i++) { 452 smb_init_name_struct((unsigned char *)domain, suffix[i], 453 0, 0, 0, 0, 0, &dname); 454 455 syslog(LOG_DEBUG, "smb_netlogon_send"); 456 smb_netbios_name_dump(&dname); 457 if ((dest = smb_name_find_name(&dname)) != 0) { 458 dest_dup = smb_netbios_name_dup(dest, 1); 459 smb_name_unlock_name(dest); 460 if (dest_dup) { 461 (void) smb_netbios_datagram_send(name, 462 dest_dup, buffer, count); 463 free(dest_dup); 464 } 465 } else { 466 syslog(LOG_DEBUG, "smbd: NBNS couldn't find %s<0x%X>", 467 domain, suffix[i]); 468 } 469 } 470 } 471 472 /* 473 * smb_netlogon_rdc_rsp 474 * 475 * This is where we process netlogon responses for the resource domain. 476 * The src_name is the real name of the remote machine. 477 */ 478 static void 479 smb_netlogon_rdc_rsp(char *src_name, uint32_t src_ipaddr) 480 { 481 static int initialized = 0; 482 uint32_t ipaddr; 483 uint32_t prefer_ipaddr; 484 char ipstr[INET_ADDRSTRLEN]; 485 char srcip[INET_ADDRSTRLEN]; 486 int rc; 487 488 (void) inet_ntop(AF_INET, &src_ipaddr, srcip, INET_ADDRSTRLEN); 489 490 rc = smb_config_getstr(SMB_CI_DOMAIN_SRV, ipstr, INET_ADDRSTRLEN); 491 if (rc == SMBD_SMF_OK) { 492 rc = inet_pton(AF_INET, ipstr, &prefer_ipaddr); 493 if (rc == 0) 494 prefer_ipaddr = 0; 495 496 if (!initialized) { 497 syslog(LOG_DEBUG, "SMB DC Preference: %s", ipstr); 498 initialized = 1; 499 } 500 } 501 502 (void) mutex_lock(&ntdomain_mtx); 503 syslog(LOG_DEBUG, "DC Offer [%s]: %s [%s]", 504 ntdomain_info.n_domain, src_name, srcip); 505 506 if (ntdomain_info.n_ipaddr != 0) { 507 if (prefer_ipaddr != 0 && 508 prefer_ipaddr == ntdomain_info.n_ipaddr) { 509 syslog(LOG_DEBUG, "DC for %s: %s [%s]", 510 ntdomain_info.n_domain, src_name, srcip); 511 (void) mutex_unlock(&ntdomain_mtx); 512 return; 513 } 514 515 ipaddr = ntdomain_info.n_ipaddr; 516 } else 517 ipaddr = 0; 518 519 if (smb_better_dc(ipaddr, src_ipaddr) || 520 (prefer_ipaddr != 0 && prefer_ipaddr == src_ipaddr)) { 521 /* set nbtd cache */ 522 (void) strlcpy(ntdomain_info.n_name, src_name, 523 SMB_PI_MAX_DOMAIN); 524 ntdomain_info.n_ipaddr = src_ipaddr; 525 (void) cond_broadcast(&ntdomain_cv); 526 syslog(LOG_DEBUG, "DC discovered for %s: %s [%s]", 527 ntdomain_info.n_domain, src_name, srcip); 528 } 529 (void) mutex_unlock(&ntdomain_mtx); 530 } 531 532 static int 533 smb_better_dc(uint32_t cur_ip, uint32_t new_ip) 534 { 535 smb_inaddr_t ipaddr; 536 537 /* 538 * If we don't have any current DC, 539 * then use the new one of course. 540 */ 541 542 if (cur_ip == 0) 543 return (1); 544 /* 545 * see if there is a DC in the 546 * same subnet 547 */ 548 549 ipaddr.a_family = AF_INET; 550 ipaddr.a_ipv4 = cur_ip; 551 if (smb_nic_is_same_subnet(&ipaddr)) 552 return (0); 553 554 ipaddr.a_family = AF_INET; 555 ipaddr.a_ipv4 = new_ip; 556 if (smb_nic_is_same_subnet(&ipaddr)) 557 return (1); 558 /* 559 * Otherwise, just keep the old one. 560 */ 561 return (0); 562 } 563