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 getdc = skip_string(user,1); 106 q = skip_string(getdc,1); 107 token = SVAL(q,3); 108 109 fstrcpy(reply_name,my_name); 110 111 pull_ascii_fstring(mach_str, machine); 112 pull_ascii_fstring(user_str, user); 113 pull_ascii_fstring(getdc_str, getdc); 114 115 DEBUG(5,("process_logon_packet: Domain login request from %s at IP %s user=%s token=%x\n", 116 mach_str,inet_ntoa(p->ip),user_str,token)); 117 118 q = outbuf; 119 SSVAL(q, 0, 6); 120 q += 2; 121 122 fstrcpy(reply_name, "\\\\"); 123 fstrcat(reply_name, my_name); 124 push_ascii_fstring(q, reply_name); 125 q = skip_string(q, 1); /* PDC name */ 126 127 SSVAL(q, 0, token); 128 q += 2; 129 130 dump_data(4, outbuf, PTR_DIFF(q, outbuf)); 131 132 send_mailslot(True, getdc_str, 133 outbuf,PTR_DIFF(q,outbuf), 134 global_myname(), 0x0, 135 mach_str, 136 dgram->source_name.name_type, 137 p->ip, *iface_ip(p->ip), p->port); 138 break; 139 } 140 141 case QUERYFORPDC: 142 { 143 fstring mach_str, getdc_str; 144 nstring source_name; 145 char *q = buf + 2; 146 char *machine = q; 147 148 if (!lp_domain_master()) { 149 /* We're not Primary Domain Controller -- ignore this */ 150 return; 151 } 152 153 getdc = skip_string(machine,1); 154 q = skip_string(getdc,1); 155 q = ALIGN2(q, buf); 156 157 /* At this point we can work out if this is a W9X or NT style 158 request. Experiments show that the difference is wether the 159 packet ends here. For a W9X request we now end with a pair of 160 bytes (usually 0xFE 0xFF) whereas with NT we have two further 161 strings - the following is a simple way of detecting this */ 162 163 if (len - PTR_DIFF(q, buf) <= 3) { 164 short_request = True; 165 } else { 166 unicomp = q; 167 168 /* A full length (NT style) request */ 169 q = skip_unibuf(unicomp, PTR_DIFF(buf + len, unicomp)); 170 171 if (len - PTR_DIFF(q, buf) > 8) { 172 /* with NT5 clients we can sometimes 173 get additional data - a length specificed string 174 containing the domain name, then 16 bytes of 175 data (no idea what it is) */ 176 int dom_len = CVAL(q, 0); 177 q++; 178 if (dom_len != 0) { 179 q += dom_len + 1; 180 } 181 q += 16; 182 } 183 ntversion = IVAL(q, 0); 184 lmnttoken = SVAL(q, 4); 185 lm20token = SVAL(q, 6); 186 } 187 188 /* Construct reply. */ 189 q = outbuf; 190 SSVAL(q, 0, QUERYFORPDC_R); 191 q += 2; 192 193 fstrcpy(reply_name,my_name); 194 push_ascii_fstring(q, reply_name); 195 q = skip_string(q, 1); /* PDC name */ 196 197 /* PDC and domain name */ 198 if (!short_request) { 199 /* Make a full reply */ 200 q = ALIGN2(q, outbuf); 201 202 q += dos_PutUniCode(q, my_name, sizeof(pstring), True); /* PDC name */ 203 q += dos_PutUniCode(q, lp_workgroup(),sizeof(pstring), True); /* Domain name*/ 204 SIVAL(q, 0, 1); /* our nt version */ 205 SSVAL(q, 4, 0xffff); /* our lmnttoken */ 206 SSVAL(q, 6, 0xffff); /* our lm20token */ 207 q += 8; 208 } 209 210 /* RJS, 21-Feb-2000, we send a short reply if the request was short */ 211 212 pull_ascii_fstring(mach_str, machine); 213 214 DEBUG(5,("process_logon_packet: GETDC request from %s at IP %s, \ 215reporting %s domain %s 0x%x ntversion=%x lm_nt token=%x lm_20 token=%x\n", 216 mach_str,inet_ntoa(p->ip), reply_name, lp_workgroup(), 217 QUERYFORPDC_R, (uint32)ntversion, (uint32)lmnttoken, 218 (uint32)lm20token )); 219 220 dump_data(4, outbuf, PTR_DIFF(q, outbuf)); 221 222 pull_ascii_fstring(getdc_str, getdc); 223 pull_ascii_nstring(source_name, dgram->source_name.name); 224 225 send_mailslot(True, getdc_str, 226 outbuf,PTR_DIFF(q,outbuf), 227 global_myname(), 0x0, 228 source_name, 229 dgram->source_name.name_type, 230 p->ip, *iface_ip(p->ip), p->port); 231 return; 232 } 233 234 case SAMLOGON: 235 236 { 237 fstring getdc_str; 238 nstring source_name; 239 char *q = buf + 2; 240 fstring asccomp; 241 242 q += 2; 243 unicomp = q; 244 uniuser = skip_unibuf(unicomp, PTR_DIFF(buf+len, unicomp)); 245 getdc = skip_unibuf(uniuser,PTR_DIFF(buf+len, uniuser)); 246 q = skip_string(getdc,1); 247 q += 4; /* Account Control Bits - indicating username type */ 248 domainsidsize = IVAL(q, 0); 249 q += 4; 250 251 DEBUG(5,("process_logon_packet: SAMLOGON sidsize %d, len = %d\n", domainsidsize, len)); 252 253 if (domainsidsize < (len - PTR_DIFF(q, buf)) && (domainsidsize != 0)) { 254 q += domainsidsize; 255 q = ALIGN4(q, buf); 256 } 257 258 DEBUG(5,("process_logon_packet: len = %d PTR_DIFF(q, buf) = %ld\n", len, (unsigned long)PTR_DIFF(q, buf) )); 259 260 if (len - PTR_DIFF(q, buf) > 8) { 261 /* with NT5 clients we can sometimes 262 get additional data - a length specificed string 263 containing the domain name, then 16 bytes of 264 data (no idea what it is) */ 265 int dom_len = CVAL(q, 0); 266 q++; 267 if (dom_len < (len - PTR_DIFF(q, buf)) && (dom_len != 0)) { 268 q += dom_len + 1; 269 } 270 q += 16; 271 } 272 273 ntversion = IVAL(q, 0); 274 lmnttoken = SVAL(q, 4); 275 lm20token = SVAL(q, 6); 276 q += 8; 277 278 DEBUG(3,("process_logon_packet: SAMLOGON sidsize %d ntv %d\n", domainsidsize, ntversion)); 279 280 /* 281 * we respond regadless of whether the machine is in our password 282 * database. If it isn't then we let smbd send an appropriate error. 283 * Let's ignore the SID. 284 */ 285 pull_ucs2_pstring(ascuser, uniuser); 286 pull_ucs2_fstring(asccomp, unicomp); 287 DEBUG(5,("process_logon_packet: SAMLOGON user %s\n", ascuser)); 288 289 fstrcpy(reply_name, "\\\\"); /* Here it wants \\LOGONSERVER. */ 290 fstrcat(reply_name, my_name); 291 292 DEBUG(5,("process_logon_packet: SAMLOGON request from %s(%s) for %s, returning logon svr %s domain %s code %x token=%x\n", 293 asccomp,inet_ntoa(p->ip), ascuser, reply_name, lp_workgroup(), 294 SAMLOGON_R ,lmnttoken)); 295 296 /* Construct reply. */ 297 298 q = outbuf; 299 /* we want the simple version unless we are an ADS PDC..which means */ 300 /* never, at least for now */ 301 if ((ntversion < 11) || (SEC_ADS != lp_security()) || (ROLE_DOMAIN_PDC != lp_server_role())) { 302 if (SVAL(uniuser, 0) == 0) { 303 SSVAL(q, 0, SAMLOGON_UNK_R); /* user unknown */ 304 } else { 305 SSVAL(q, 0, SAMLOGON_R); 306 } 307 308 q += 2; 309 310 q += dos_PutUniCode(q, reply_name,sizeof(pstring), True); 311 q += dos_PutUniCode(q, ascuser, sizeof(pstring), True); 312 q += dos_PutUniCode(q, lp_workgroup(),sizeof(pstring), True); 313 } 314#ifdef HAVE_ADS 315 else { 316 GUID domain_guid; 317 pstring domain; 318 pstring hostname; 319 char *component, *dc, *q1; 320 uint8 size; 321 char *q_orig = q; 322 int str_offset; 323 324 get_mydomname(domain); 325 get_myname(hostname); 326 327 if (SVAL(uniuser, 0) == 0) { 328 SIVAL(q, 0, SAMLOGON_AD_UNK_R); /* user unknown */ 329 } else { 330 SIVAL(q, 0, SAMLOGON_AD_R); 331 } 332 q += 4; 333 334 SIVAL(q, 0, ADS_PDC|ADS_GC|ADS_LDAP|ADS_DS| 335 ADS_KDC|ADS_TIMESERV|ADS_CLOSEST|ADS_WRITABLE); 336 q += 4; 337 338 /* Push Domain GUID */ 339 if (False == secrets_fetch_domain_guid(domain, &domain_guid)) { 340 DEBUG(2, ("Could not fetch DomainGUID for %s\n", domain)); 341 return; 342 } 343 memcpy(q, &domain_guid, sizeof(domain_guid)); 344 q += sizeof(domain_guid); 345 346 /* Forest */ 347 str_offset = q - q_orig; 348 dc = domain; 349 q1 = q; 350 while ((component = strtok(dc, "."))) { 351 dc = NULL; 352 size = push_ascii(&q[1], component, -1, 0); 353 SCVAL(q, 0, size); 354 q += (size + 1); 355 } 356 357 /* Unk0 */ 358 SCVAL(q, 0, 0); 359 q++; 360 361 /* Domain */ 362 SCVAL(q, 0, 0xc0 | ((str_offset >> 8) & 0x3F)); 363 SCVAL(q, 1, str_offset & 0xFF); 364 q += 2; 365 366 /* Hostname */ 367 size = push_ascii(&q[1], hostname, -1, 0); 368 SCVAL(q, 0, size); 369 q += (size + 1); 370 SCVAL(q, 0, 0xc0 | ((str_offset >> 8) & 0x3F)); 371 SCVAL(q, 1, str_offset & 0xFF); 372 q += 2; 373 374 /* NETBIOS of domain */ 375 size = push_ascii(&q[1], lp_workgroup(), -1, STR_UPPER); 376 SCVAL(q, 0, size); 377 q += (size + 1); 378 379 /* Unk1 */ 380 SCVAL(q, 0, 0); 381 q++; 382 383 /* NETBIOS of hostname */ 384 size = push_ascii(&q[1], my_name, -1, 0); 385 SCVAL(q, 0, size); 386 q += (size + 1); 387 388 /* Unk2 */ 389 SCVAL(q, 0, 0); 390 q++; 391 392 /* User name */ 393 if (SVAL(uniuser, 0) != 0) { 394 size = push_ascii(&q[1], ascuser, -1, 0); 395 SCVAL(q, 0, size); 396 q += (size + 1); 397 } 398 399 q_orig = q; 400 /* Site name */ 401 size = push_ascii(&q[1], "Default-First-Site-Name", -1, 0); 402 SCVAL(q, 0, size); 403 q += (size + 1); 404 405 /* Site name (2) */ 406 str_offset = q - q_orig; 407 SCVAL(q, 0, 0xc0 | ((str_offset >> 8) & 0x3F)); 408 SCVAL(q, 1, str_offset & 0xFF); 409 q += 2; 410 411 SCVAL(q, 0, PTR_DIFF(q,q1)); 412 SCVAL(q, 1, 0x10); /* unknown */ 413 414 SIVAL(q, 0, 0x00000002); 415 q += 4; /* unknown */ 416 SIVAL(q, 0, (iface_ip(p->ip))->s_addr); 417 q += 4; 418 SIVAL(q, 0, 0x00000000); 419 q += 4; /* unknown */ 420 SIVAL(q, 0, 0x00000000); 421 q += 4; /* unknown */ 422 } 423#endif 424 425 /* tell the client what version we are */ 426 SIVAL(q, 0, ((ntversion < 11) || (SEC_ADS != lp_security())) ? 1 : 13); 427 /* our ntversion */ 428 SSVAL(q, 4, 0xffff); /* our lmnttoken */ 429 SSVAL(q, 6, 0xffff); /* our lm20token */ 430 q += 8; 431 432 dump_data(4, outbuf, PTR_DIFF(q, outbuf)); 433 434 pull_ascii_fstring(getdc_str, getdc); 435 pull_ascii_nstring(source_name, dgram->source_name.name); 436 437 send_mailslot(True, getdc, 438 outbuf,PTR_DIFF(q,outbuf), 439 global_myname(), 0x0, 440 dgram->source_name.name, 441 dgram->source_name.name_type, 442 p->ip, *iface_ip(p->ip), p->port); 443 break; 444 } 445 446 /* Announce change to UAS or SAM. Send by the domain controller when a 447 replication event is required. */ 448 449 case SAM_UAS_CHANGE: 450 { 451 struct sam_database_info *db_info; 452 char *q = buf + 2; 453 int i, db_count; 454 uint32 low_serial; 455 456 /* Header */ 457 458 low_serial = IVAL(q, 0); q += 4; /* Low serial number */ 459 460 q += 4; /* Date/time */ 461 q += 4; /* Pulse */ 462 q += 4; /* Random */ 463 464 /* Domain info */ 465 466 q = skip_string(q, 1); /* PDC name */ 467 q = skip_string(q, 1); /* Domain name */ 468 q = skip_unibuf(q, PTR_DIFF(buf + len, q)); /* Unicode PDC name */ 469 q = skip_unibuf(q, PTR_DIFF(buf + len, q)); /* Unicode domain name */ 470 471 /* Database info */ 472 473 db_count = SVAL(q, 0); q += 2; 474 475 db_info = (struct sam_database_info *) 476 malloc(sizeof(struct sam_database_info) * db_count); 477 478 if (db_info == NULL) { 479 DEBUG(3, ("out of memory allocating info for %d databases\n", db_count)); 480 return; 481 } 482 483 for (i = 0; i < db_count; i++) { 484 db_info[i].index = IVAL(q, 0); 485 db_info[i].serial_lo = IVAL(q, 4); 486 db_info[i].serial_hi = IVAL(q, 8); 487 db_info[i].date_lo = IVAL(q, 12); 488 db_info[i].date_hi = IVAL(q, 16); 489 q += 20; 490 } 491 492 /* Domain SID */ 493 494#if 0 495 /* We must range check this. */ 496 q += IVAL(q, 0) + 4; /* 4 byte length plus data */ 497 498 q += 2; /* Alignment? */ 499 500 /* Misc other info */ 501 502 q += 4; /* NT version (0x1) */ 503 q += 2; /* LMNT token (0xff) */ 504 q += 2; /* LM20 token (0xff) */ 505#endif 506 507 SAFE_FREE(db_info); /* Not sure whether we need to do anything useful with these */ 508 509 /* Send message to smbd */ 510 511 send_repl_message(low_serial); 512 break; 513 } 514 515 default: 516 DEBUG(3,("process_logon_packet: Unknown domain request %d\n",code)); 517 return; 518 } 519} 520