1/* 2 Unix SMB/CIFS implementation. 3 NBT netbios routines and daemon - version 2 4 Copyright (C) Andrew Tridgell 1994-1998 5 Copyright (C) Luke Kenneth Casson Leighton 1994-1998 6 Copyright (C) Jeremy Allison 1994-2003 7 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002 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 2 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, write to the Free Software 21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 23 Revision History: 24 25*/ 26 27#include "includes.h" 28 29struct sam_database_info { 30 uint32 index; 31 uint32 serial_lo, serial_hi; 32 uint32 date_lo, date_hi; 33}; 34 35/**************************************************************************** 36Send a message to smbd to do a sam delta sync 37**************************************************************************/ 38 39static void send_repl_message(uint32 low_serial) 40{ 41 TDB_CONTEXT *tdb; 42 43 tdb = tdb_open_log(lock_path("connections.tdb"), 0, 44 TDB_DEFAULT, O_RDONLY, 0); 45 46 if (!tdb) { 47 DEBUG(3, ("send_repl_message(): failed to open connections " 48 "database\n")); 49 return; 50 } 51 52 DEBUG(3, ("sending replication message, serial = 0x%04x\n", 53 low_serial)); 54 55 message_send_all(tdb, MSG_SMB_SAM_REPL, &low_serial, 56 sizeof(low_serial), False, NULL); 57 58 tdb_close(tdb); 59} 60 61/**************************************************************************** 62Process a domain logon packet 63**************************************************************************/ 64 65void process_logon_packet(struct packet_struct *p, char *buf,int len, 66 const char *mailslot) 67{ 68 struct dgram_packet *dgram = &p->packet.dgram; 69 pstring my_name; 70 fstring reply_name; 71 pstring outbuf; 72 int code; 73 uint16 token = 0; 74 uint32 ntversion = 0; 75 uint16 lmnttoken = 0; 76 uint16 lm20token = 0; 77 uint32 domainsidsize; 78 BOOL short_request = False; 79 char *getdc; 80 char *uniuser; /* Unicode user name. */ 81 pstring ascuser; 82 char *unicomp; /* Unicode computer name. */ 83 84 memset(outbuf, 0, sizeof(outbuf)); 85 86 if (!lp_domain_logons()) { 87 DEBUG(5,("process_logon_packet: Logon packet received from IP %s and domain \ 88logons are not enabled.\n", inet_ntoa(p->ip) )); 89 return; 90 } 91 92 pstrcpy(my_name, global_myname()); 93 94 code = SVAL(buf,0); 95 DEBUG(4,("process_logon_packet: Logon from %s: code = 0x%x\n", inet_ntoa(p->ip), code)); 96 97 switch (code) { 98 case 0: 99 { 100 fstring mach_str, user_str, getdc_str; 101 char *q = buf + 2; 102 char *machine = q; 103 char *user = skip_string(machine,1); 104 105 if (PTR_DIFF(user, buf) >= len) { 106 DEBUG(0,("process_logon_packet: bad packet\n")); 107 return; 108 } 109 getdc = skip_string(user,1); 110 111 if (PTR_DIFF(getdc, buf) >= len) { 112 DEBUG(0,("process_logon_packet: bad packet\n")); 113 return; 114 } 115 q = skip_string(getdc,1); 116 117 if (PTR_DIFF(q + 5, buf) > len) { 118 DEBUG(0,("process_logon_packet: bad packet\n")); 119 return; 120 } 121 token = SVAL(q,3); 122 123 fstrcpy(reply_name,my_name); 124 125 pull_ascii_fstring(mach_str, machine); 126 pull_ascii_fstring(user_str, user); 127 pull_ascii_fstring(getdc_str, getdc); 128 129 DEBUG(5,("process_logon_packet: Domain login request from %s at IP %s user=%s token=%x\n", 130 mach_str,inet_ntoa(p->ip),user_str,token)); 131 132 q = outbuf; 133 SSVAL(q, 0, 6); 134 q += 2; 135 136 fstrcpy(reply_name, "\\\\"); 137 fstrcat(reply_name, my_name); 138 push_ascii_fstring(q, reply_name); 139 q = skip_string(q, 1); /* PDC name */ 140 141 SSVAL(q, 0, token); 142 q += 2; 143 144 dump_data(4, outbuf, PTR_DIFF(q, outbuf)); 145 146 send_mailslot(True, getdc_str, 147 outbuf,PTR_DIFF(q,outbuf), 148 global_myname(), 0x0, 149 mach_str, 150 dgram->source_name.name_type, 151 p->ip, *iface_ip(p->ip), p->port); 152 break; 153 } 154 155 case QUERYFORPDC: 156 { 157 fstring mach_str, getdc_str; 158 fstring source_name; 159 char *q = buf + 2; 160 char *machine = q; 161 162 if (!lp_domain_master()) { 163 /* We're not Primary Domain Controller -- ignore this */ 164 return; 165 } 166 167 getdc = skip_string(machine,1); 168 169 if (PTR_DIFF(getdc, buf) >= len) { 170 DEBUG(0,("process_logon_packet: bad packet\n")); 171 return; 172 } 173 q = skip_string(getdc,1); 174 175 if (PTR_DIFF(q, buf) >= len) { 176 DEBUG(0,("process_logon_packet: bad packet\n")); 177 return; 178 } 179 q = ALIGN2(q, buf); 180 181 /* At this point we can work out if this is a W9X or NT style 182 request. Experiments show that the difference is wether the 183 packet ends here. For a W9X request we now end with a pair of 184 bytes (usually 0xFE 0xFF) whereas with NT we have two further 185 strings - the following is a simple way of detecting this */ 186 187 if (len - PTR_DIFF(q, buf) <= 3) { 188 short_request = True; 189 } else { 190 unicomp = q; 191 192 if (PTR_DIFF(q, buf) >= len) { 193 DEBUG(0,("process_logon_packet: bad packet\n")); 194 return; 195 } 196 197 /* A full length (NT style) request */ 198 q = skip_unibuf(unicomp, PTR_DIFF(buf + len, unicomp)); 199 200 if (PTR_DIFF(q, buf) >= len) { 201 DEBUG(0,("process_logon_packet: bad packet\n")); 202 return; 203 } 204 205 if (len - PTR_DIFF(q, buf) > 8) { 206 /* with NT5 clients we can sometimes 207 get additional data - a length specificed string 208 containing the domain name, then 16 bytes of 209 data (no idea what it is) */ 210 int dom_len = CVAL(q, 0); 211 q++; 212 if (dom_len != 0) { 213 q += dom_len + 1; 214 } 215 q += 16; 216 } 217 218 if (PTR_DIFF(q + 8, buf) > len) { 219 DEBUG(0,("process_logon_packet: bad packet\n")); 220 return; 221 } 222 223 ntversion = IVAL(q, 0); 224 lmnttoken = SVAL(q, 4); 225 lm20token = SVAL(q, 6); 226 } 227 228 /* Construct reply. */ 229 q = outbuf; 230 SSVAL(q, 0, QUERYFORPDC_R); 231 q += 2; 232 233 fstrcpy(reply_name,my_name); 234 push_ascii_fstring(q, reply_name); 235 q = skip_string(q, 1); /* PDC name */ 236 237 /* PDC and domain name */ 238 if (!short_request) { 239 /* Make a full reply */ 240 q = ALIGN2(q, outbuf); 241 242 q += dos_PutUniCode(q, my_name, sizeof(pstring), True); /* PDC name */ 243 q += dos_PutUniCode(q, lp_workgroup(),sizeof(pstring), True); /* Domain name*/ 244 SIVAL(q, 0, 1); /* our nt version */ 245 SSVAL(q, 4, 0xffff); /* our lmnttoken */ 246 SSVAL(q, 6, 0xffff); /* our lm20token */ 247 q += 8; 248 } 249 250 /* RJS, 21-Feb-2000, we send a short reply if the request was short */ 251 252 pull_ascii_fstring(mach_str, machine); 253 254 DEBUG(5,("process_logon_packet: GETDC request from %s at IP %s, \ 255reporting %s domain %s 0x%x ntversion=%x lm_nt token=%x lm_20 token=%x\n", 256 mach_str,inet_ntoa(p->ip), reply_name, lp_workgroup(), 257 QUERYFORPDC_R, (uint32)ntversion, (uint32)lmnttoken, 258 (uint32)lm20token )); 259 260 dump_data(4, outbuf, PTR_DIFF(q, outbuf)); 261 262 pull_ascii_fstring(getdc_str, getdc); 263 pull_ascii_nstring(source_name, sizeof(source_name), dgram->source_name.name); 264 265 send_mailslot(True, getdc_str, 266 outbuf,PTR_DIFF(q,outbuf), 267 global_myname(), 0x0, 268 source_name, 269 dgram->source_name.name_type, 270 p->ip, *iface_ip(p->ip), p->port); 271 return; 272 } 273 274 case SAMLOGON: 275 276 { 277 fstring getdc_str; 278 fstring source_name; 279 char *q = buf + 2; 280 fstring asccomp; 281 282 q += 2; 283 284 if (PTR_DIFF(q, buf) >= len) { 285 DEBUG(0,("process_logon_packet: bad packet\n")); 286 return; 287 } 288 289 unicomp = q; 290 uniuser = skip_unibuf(unicomp, PTR_DIFF(buf+len, unicomp)); 291 292 if (PTR_DIFF(uniuser, buf) >= len) { 293 DEBUG(0,("process_logon_packet: bad packet\n")); 294 return; 295 } 296 297 getdc = skip_unibuf(uniuser,PTR_DIFF(buf+len, uniuser)); 298 299 if (PTR_DIFF(getdc, buf) >= len) { 300 DEBUG(0,("process_logon_packet: bad packet\n")); 301 return; 302 } 303 304 q = skip_string(getdc,1); 305 306 if (PTR_DIFF(q + 8, buf) >= len) { 307 DEBUG(0,("process_logon_packet: bad packet\n")); 308 return; 309 } 310 311 q += 4; /* Account Control Bits - indicating username type */ 312 domainsidsize = IVAL(q, 0); 313 q += 4; 314 315 DEBUG(5,("process_logon_packet: SAMLOGON sidsize %d, len = %d\n", domainsidsize, len)); 316 317 if (domainsidsize < (len - PTR_DIFF(q, buf)) && (domainsidsize != 0)) { 318 q += domainsidsize; 319 q = ALIGN4(q, buf); 320 } 321 322 DEBUG(5,("process_logon_packet: len = %d PTR_DIFF(q, buf) = %ld\n", len, (unsigned long)PTR_DIFF(q, buf) )); 323 324 if (len - PTR_DIFF(q, buf) > 8) { 325 /* with NT5 clients we can sometimes 326 get additional data - a length specificed string 327 containing the domain name, then 16 bytes of 328 data (no idea what it is) */ 329 int dom_len = CVAL(q, 0); 330 q++; 331 if (dom_len < (len - PTR_DIFF(q, buf)) && (dom_len != 0)) { 332 q += dom_len + 1; 333 } 334 q += 16; 335 } 336 337 if (PTR_DIFF(q + 8, buf) > len) { 338 DEBUG(0,("process_logon_packet: bad packet\n")); 339 return; 340 } 341 342 ntversion = IVAL(q, 0); 343 lmnttoken = SVAL(q, 4); 344 lm20token = SVAL(q, 6); 345 q += 8; 346 347 DEBUG(3,("process_logon_packet: SAMLOGON sidsize %d ntv %d\n", domainsidsize, ntversion)); 348 349 /* 350 * we respond regadless of whether the machine is in our password 351 * database. If it isn't then we let smbd send an appropriate error. 352 * Let's ignore the SID. 353 */ 354 pull_ucs2_pstring(ascuser, uniuser); 355 pull_ucs2_fstring(asccomp, unicomp); 356 DEBUG(5,("process_logon_packet: SAMLOGON user %s\n", ascuser)); 357 358 fstrcpy(reply_name, "\\\\"); /* Here it wants \\LOGONSERVER. */ 359 fstrcat(reply_name, my_name); 360 361 DEBUG(5,("process_logon_packet: SAMLOGON request from %s(%s) for %s, returning logon svr %s domain %s code %x token=%x\n", 362 asccomp,inet_ntoa(p->ip), ascuser, reply_name, lp_workgroup(), 363 SAMLOGON_R ,lmnttoken)); 364 365 /* Construct reply. */ 366 367 q = outbuf; 368 /* we want the simple version unless we are an ADS PDC..which means */ 369 /* never, at least for now */ 370 if ((ntversion < 11) || (SEC_ADS != lp_security()) || (ROLE_DOMAIN_PDC != lp_server_role())) { 371 if (SVAL(uniuser, 0) == 0) { 372 SSVAL(q, 0, SAMLOGON_UNK_R); /* user unknown */ 373 } else { 374 SSVAL(q, 0, SAMLOGON_R); 375 } 376 377 q += 2; 378 379 q += dos_PutUniCode(q, reply_name,sizeof(pstring), True); 380 q += dos_PutUniCode(q, ascuser, sizeof(pstring), True); 381 q += dos_PutUniCode(q, lp_workgroup(),sizeof(pstring), True); 382 } 383#ifdef HAVE_ADS 384 else { 385 struct uuid domain_guid; 386 UUID_FLAT flat_guid; 387 pstring domain; 388 pstring hostname; 389 char *component, *dc, *q1; 390 uint8 size; 391 char *q_orig = q; 392 int str_offset; 393 394 get_mydnsdomname(domain); 395 get_myname(hostname); 396 397 if (SVAL(uniuser, 0) == 0) { 398 SIVAL(q, 0, SAMLOGON_AD_UNK_R); /* user unknown */ 399 } else { 400 SIVAL(q, 0, SAMLOGON_AD_R); 401 } 402 q += 4; 403 404 SIVAL(q, 0, ADS_PDC|ADS_GC|ADS_LDAP|ADS_DS| 405 ADS_KDC|ADS_TIMESERV|ADS_CLOSEST|ADS_WRITABLE); 406 q += 4; 407 408 /* Push Domain GUID */ 409 if (False == secrets_fetch_domain_guid(domain, &domain_guid)) { 410 DEBUG(2, ("Could not fetch DomainGUID for %s\n", domain)); 411 return; 412 } 413 414 smb_uuid_pack(domain_guid, &flat_guid); 415 memcpy(q, &flat_guid.info, UUID_FLAT_SIZE); 416 q += UUID_FLAT_SIZE; 417 418 /* Forest */ 419 str_offset = q - q_orig; 420 dc = domain; 421 q1 = q; 422 while ((component = strtok(dc, "."))) { 423 dc = NULL; 424 size = push_ascii(&q[1], component, -1, 0); 425 SCVAL(q, 0, size); 426 q += (size + 1); 427 } 428 429 /* Unk0 */ 430 SCVAL(q, 0, 0); 431 q++; 432 433 /* Domain */ 434 SCVAL(q, 0, 0xc0 | ((str_offset >> 8) & 0x3F)); 435 SCVAL(q, 1, str_offset & 0xFF); 436 q += 2; 437 438 /* Hostname */ 439 size = push_ascii(&q[1], hostname, -1, 0); 440 SCVAL(q, 0, size); 441 q += (size + 1); 442 SCVAL(q, 0, 0xc0 | ((str_offset >> 8) & 0x3F)); 443 SCVAL(q, 1, str_offset & 0xFF); 444 q += 2; 445 446 /* NETBIOS of domain */ 447 size = push_ascii(&q[1], lp_workgroup(), -1, STR_UPPER); 448 SCVAL(q, 0, size); 449 q += (size + 1); 450 451 /* Unk1 */ 452 SCVAL(q, 0, 0); 453 q++; 454 455 /* NETBIOS of hostname */ 456 size = push_ascii(&q[1], my_name, -1, 0); 457 SCVAL(q, 0, size); 458 q += (size + 1); 459 460 /* Unk2 */ 461 SCVAL(q, 0, 0); 462 q++; 463 464 /* User name */ 465 if (SVAL(uniuser, 0) != 0) { 466 size = push_ascii(&q[1], ascuser, -1, 0); 467 SCVAL(q, 0, size); 468 q += (size + 1); 469 } 470 471 q_orig = q; 472 /* Site name */ 473 size = push_ascii(&q[1], "Default-First-Site-Name", -1, 0); 474 SCVAL(q, 0, size); 475 q += (size + 1); 476 477 /* Site name (2) */ 478 str_offset = q - q_orig; 479 SCVAL(q, 0, 0xc0 | ((str_offset >> 8) & 0x3F)); 480 SCVAL(q, 1, str_offset & 0xFF); 481 q += 2; 482 483 SCVAL(q, 0, PTR_DIFF(q,q1)); 484 SCVAL(q, 1, 0x10); /* unknown */ 485 486 SIVAL(q, 0, 0x00000002); 487 q += 4; /* unknown */ 488 SIVAL(q, 0, (iface_ip(p->ip))->s_addr); 489 q += 4; 490 SIVAL(q, 0, 0x00000000); 491 q += 4; /* unknown */ 492 SIVAL(q, 0, 0x00000000); 493 q += 4; /* unknown */ 494 } 495#endif 496 497 /* tell the client what version we are */ 498 SIVAL(q, 0, ((ntversion < 11) || (SEC_ADS != lp_security())) ? 1 : 13); 499 /* our ntversion */ 500 SSVAL(q, 4, 0xffff); /* our lmnttoken */ 501 SSVAL(q, 6, 0xffff); /* our lm20token */ 502 q += 8; 503 504 dump_data(4, outbuf, PTR_DIFF(q, outbuf)); 505 506 pull_ascii_fstring(getdc_str, getdc); 507 pull_ascii_nstring(source_name, sizeof(source_name), dgram->source_name.name); 508 509 send_mailslot(True, getdc, 510 outbuf,PTR_DIFF(q,outbuf), 511 global_myname(), 0x0, 512 source_name, 513 dgram->source_name.name_type, 514 p->ip, *iface_ip(p->ip), p->port); 515 break; 516 } 517 518 /* Announce change to UAS or SAM. Send by the domain controller when a 519 replication event is required. */ 520 521 case SAM_UAS_CHANGE: 522 { 523 struct sam_database_info *db_info; 524 char *q = buf + 2; 525 int i, db_count; 526 uint32 low_serial; 527 528 /* Header */ 529 530 if (PTR_DIFF(q + 16, buf) >= len) { 531 DEBUG(0,("process_logon_packet: bad packet\n")); 532 return; 533 } 534 535 low_serial = IVAL(q, 0); q += 4; /* Low serial number */ 536 537 q += 4; /* Date/time */ 538 q += 4; /* Pulse */ 539 q += 4; /* Random */ 540 541 /* Domain info */ 542 543 q = skip_string(q, 1); /* PDC name */ 544 545 if (PTR_DIFF(q, buf) >= len) { 546 DEBUG(0,("process_logon_packet: bad packet\n")); 547 return; 548 } 549 550 q = skip_string(q, 1); /* Domain name */ 551 552 if (PTR_DIFF(q, buf) >= len) { 553 DEBUG(0,("process_logon_packet: bad packet\n")); 554 return; 555 } 556 557 q = skip_unibuf(q, PTR_DIFF(buf + len, q)); /* Unicode PDC name */ 558 559 if (PTR_DIFF(q, buf) >= len) { 560 DEBUG(0,("process_logon_packet: bad packet\n")); 561 return; 562 } 563 564 q = skip_unibuf(q, PTR_DIFF(buf + len, q)); /* Unicode domain name */ 565 566 /* Database info */ 567 568 if (PTR_DIFF(q + 2, buf) >= len) { 569 DEBUG(0,("process_logon_packet: bad packet\n")); 570 return; 571 } 572 573 db_count = SVAL(q, 0); q += 2; 574 575 if (PTR_DIFF(q + (db_count*20), buf) >= len) { 576 DEBUG(0,("process_logon_packet: bad packet\n")); 577 return; 578 } 579 580 db_info = SMB_MALLOC_ARRAY(struct sam_database_info, db_count); 581 582 if (db_info == NULL) { 583 DEBUG(3, ("out of memory allocating info for %d databases\n", db_count)); 584 return; 585 } 586 587 for (i = 0; i < db_count; i++) { 588 db_info[i].index = IVAL(q, 0); 589 db_info[i].serial_lo = IVAL(q, 4); 590 db_info[i].serial_hi = IVAL(q, 8); 591 db_info[i].date_lo = IVAL(q, 12); 592 db_info[i].date_hi = IVAL(q, 16); 593 q += 20; 594 } 595 596 /* Domain SID */ 597 598#if 0 599 /* We must range check this. */ 600 q += IVAL(q, 0) + 4; /* 4 byte length plus data */ 601 602 q += 2; /* Alignment? */ 603 604 /* Misc other info */ 605 606 q += 4; /* NT version (0x1) */ 607 q += 2; /* LMNT token (0xff) */ 608 q += 2; /* LM20 token (0xff) */ 609#endif 610 611 SAFE_FREE(db_info); /* Not sure whether we need to do anything useful with these */ 612 613 /* Send message to smbd */ 614 615 send_repl_message(low_serial); 616 break; 617 } 618 619 default: 620 DEBUG(3,("process_logon_packet: Unknown domain request %d\n",code)); 621 return; 622 } 623} 624