1/* 2 * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup 3 * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995. 4 * 5 * This program is free software; you can redistribute it and/or modify it under 6 * the terms of the GNU General Public License as published by the Free 7 * Software Foundation; either version 2 of the License, or (at your option) 8 * any later version. 9 * 10 * This program is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 * 15 * You should have received a copy of the GNU General Public License along with 16 * this program; if not, write to the Free Software Foundation, Inc., 675 17 * Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 20#include "includes.h" 21 22#ifdef USE_SMBPASS_DB 23 24extern int DEBUGLEVEL; 25extern pstring samlogon_user; 26extern BOOL sam_logon_in_ssb; 27 28static int pw_file_lock_depth; 29 30enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE }; 31 32/*************************************************************** 33 Internal fn to enumerate the smbpasswd list. Returns a void pointer 34 to ensure no modification outside this module. Checks for atomic 35 rename of smbpasswd file on update or create once the lock has 36 been granted to prevent race conditions. JRA. 37****************************************************************/ 38 39static void *startsmbfilepwent_internal(const char *pfile, enum pwf_access_type type, int *lock_depth) 40{ 41 FILE *fp = NULL; 42 const char *open_mode = NULL; 43 int race_loop = 0; 44 int lock_type; 45 46 if (!*pfile) { 47 DEBUG(0, ("startsmbfilepwent: No SMB password file set\n")); 48 return (NULL); 49 } 50 51 switch(type) { 52 case PWF_READ: 53 open_mode = "rb"; 54 lock_type = F_RDLCK; 55 break; 56 case PWF_UPDATE: 57 open_mode = "r+b"; 58 lock_type = F_WRLCK; 59 break; 60 case PWF_CREATE: 61 /* 62 * Ensure atomic file creation. 63 */ 64 { 65 int i, fd = -1; 66 67 for(i = 0; i < 5; i++) { 68 if((fd = sys_open(pfile, O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600))!=-1) 69 break; 70 sys_usleep(200); /* Spin, spin... */ 71 } 72 if(fd == -1) { 73 DEBUG(0,("startsmbfilepwent_internal: too many race conditions creating file %s\n", pfile)); 74 return NULL; 75 } 76 close(fd); 77 open_mode = "r+b"; 78 lock_type = F_WRLCK; 79 break; 80 } 81 } 82 83 for(race_loop = 0; race_loop < 5; race_loop++) { 84 DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile)); 85 86 if((fp = sys_fopen(pfile, open_mode)) == NULL) { 87 DEBUG(0, ("startsmbfilepwent_internal: unable to open file %s. Error was %s\n", pfile, strerror(errno) )); 88 return NULL; 89 } 90 91 if (!pw_file_lock(fileno(fp), lock_type, 5, lock_depth)) { 92 DEBUG(0, ("startsmbfilepwent_internal: unable to lock file %s. Error was %s\n", pfile, strerror(errno) )); 93 fclose(fp); 94 return NULL; 95 } 96 97 /* 98 * Only check for replacement races on update or create. 99 * For read we don't mind if the data is one record out of date. 100 */ 101 102 if(type == PWF_READ) { 103 break; 104 } else { 105 SMB_STRUCT_STAT sbuf1, sbuf2; 106 107 /* 108 * Avoid the potential race condition between the open and the lock 109 * by doing a stat on the filename and an fstat on the fd. If the 110 * two inodes differ then someone did a rename between the open and 111 * the lock. Back off and try the open again. Only do this 5 times to 112 * prevent infinate loops. JRA. 113 */ 114 115 if (sys_stat(pfile,&sbuf1) != 0) { 116 DEBUG(0, ("startsmbfilepwent_internal: unable to stat file %s. Error was %s\n", pfile, strerror(errno))); 117 pw_file_unlock(fileno(fp), lock_depth); 118 fclose(fp); 119 return NULL; 120 } 121 122 if (sys_fstat(fileno(fp),&sbuf2) != 0) { 123 DEBUG(0, ("startsmbfilepwent_internal: unable to fstat file %s. Error was %s\n", pfile, strerror(errno))); 124 pw_file_unlock(fileno(fp), lock_depth); 125 fclose(fp); 126 return NULL; 127 } 128 129 if( sbuf1.st_ino == sbuf2.st_ino) { 130 /* No race. */ 131 break; 132 } 133 134 /* 135 * Race occurred - back off and try again... 136 */ 137 138 pw_file_unlock(fileno(fp), lock_depth); 139 fclose(fp); 140 } 141 } 142 143 if(race_loop == 5) { 144 DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile)); 145 return NULL; 146 } 147 148 /* Set a buffer to do more efficient reads */ 149 setvbuf(fp, (char *)NULL, _IOFBF, 1024); 150 151 /* Make sure it is only rw by the owner */ 152 if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) { 153 DEBUG(0, ("startsmbfilepwent_internal: failed to set 0600 permissions on password file %s. \ 154Error was %s\n.", pfile, strerror(errno) )); 155 pw_file_unlock(fileno(fp), lock_depth); 156 fclose(fp); 157 return NULL; 158 } 159 160 /* We have a lock on the file. */ 161 return (void *)fp; 162} 163 164/*************************************************************** 165 Start to enumerate the smbpasswd list. Returns a void pointer 166 to ensure no modification outside this module. 167****************************************************************/ 168 169static void *startsmbfilepwent(BOOL update) 170{ 171 return startsmbfilepwent_internal(lp_smb_passwd_file(), update ? PWF_UPDATE : PWF_READ, &pw_file_lock_depth); 172} 173 174/*************************************************************** 175 End enumeration of the smbpasswd list. 176****************************************************************/ 177 178static void endsmbfilepwent_internal(void *vp, int *lock_depth) 179{ 180 FILE *fp = (FILE *)vp; 181 182 pw_file_unlock(fileno(fp), lock_depth); 183 fclose(fp); 184 DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n")); 185} 186 187/*************************************************************** 188 End enumeration of the smbpasswd list - operate on the default 189 lock_depth. 190****************************************************************/ 191 192static void endsmbfilepwent(void *vp) 193{ 194 endsmbfilepwent_internal(vp, &pw_file_lock_depth); 195} 196 197/************************************************************************* 198 Routine to return the next entry in the smbpasswd list. 199 *************************************************************************/ 200 201static struct smb_passwd *getsmbfilepwent(void *vp) 202{ 203 /* Static buffers we will return. */ 204 static struct smb_passwd pw_buf; 205 static pstring user_name; 206 static unsigned char smbpwd[16]; 207 static unsigned char smbntpwd[16]; 208 FILE *fp = (FILE *)vp; 209 char linebuf[256]; 210 unsigned char c; 211 unsigned char *p; 212 long uidval; 213 size_t linebuf_len; 214 215 if(fp == NULL) { 216 DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n")); 217 return NULL; 218 } 219 220 pdb_init_smb(&pw_buf); 221 222 pw_buf.acct_ctrl = ACB_NORMAL; 223 224 /* 225 * Scan the file, a line at a time and check if the name matches. 226 */ 227 while (!feof(fp)) { 228 linebuf[0] = '\0'; 229 230 fgets(linebuf, 256, fp); 231 if (ferror(fp)) { 232 return NULL; 233 } 234 235 /* 236 * Check if the string is terminated with a newline - if not 237 * then we must keep reading and discard until we get one. 238 */ 239 linebuf_len = strlen(linebuf); 240 if (linebuf[linebuf_len - 1] != '\n') { 241 c = '\0'; 242 while (!ferror(fp) && !feof(fp)) { 243 c = fgetc(fp); 244 if (c == '\n') 245 break; 246 } 247 } else 248 linebuf[linebuf_len - 1] = '\0'; 249 250#ifdef DEBUG_PASSWORD 251 DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf)); 252#endif 253 if ((linebuf[0] == 0) && feof(fp)) { 254 DEBUG(4, ("getsmbfilepwent: end of file reached\n")); 255 break; 256 } 257 /* 258 * The line we have should be of the form :- 259 * 260 * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently 261 * ignored.... 262 * 263 * or, 264 * 265 * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored.... 266 * 267 * if Windows NT compatible passwords are also present. 268 * [Account type] is an ascii encoding of the type of account. 269 * LCT-(8 hex digits) is the time_t value of the last change time. 270 */ 271 272 if (linebuf[0] == '#' || linebuf[0] == '\0') { 273 DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n")); 274 continue; 275 } 276 p = (unsigned char *) strchr(linebuf, ':'); 277 if (p == NULL) { 278 DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n")); 279 continue; 280 } 281 /* 282 * As 256 is shorter than a pstring we don't need to check 283 * length here - if this ever changes.... 284 */ 285 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf)); 286 user_name[PTR_DIFF(p, linebuf)] = '\0'; 287 288 /* Get smb uid. */ 289 290 p++; /* Go past ':' */ 291 292 if(*p == '-') { 293 DEBUG(0, ("getsmbfilepwent: uids in the smbpasswd file must not be negative.\n")); 294 continue; 295 } 296 297 if (!isdigit(*p)) { 298 DEBUG(0, ("getsmbfilepwent: malformed password entry (uid not number)\n")); 299 continue; 300 } 301 302 uidval = atoi((char *) p); 303 304 while (*p && isdigit(*p)) 305 p++; 306 307 if (*p != ':') { 308 DEBUG(0, ("getsmbfilepwent: malformed password entry (no : after uid)\n")); 309 continue; 310 } 311 312 pw_buf.smb_name = user_name; 313 pw_buf.smb_userid = uidval; 314 315 /* 316 * Now get the password value - this should be 32 hex digits 317 * which are the ascii representations of a 16 byte string. 318 * Get two at a time and put them into the password. 319 */ 320 321 /* Skip the ':' */ 322 p++; 323 324 if (*p == '*' || *p == 'X') { 325 /* Password deliberately invalid - end here. */ 326 DEBUG(10, ("getsmbfilepwent: entry invalidated for user %s\n", user_name)); 327 pw_buf.smb_nt_passwd = NULL; 328 pw_buf.smb_passwd = NULL; 329 pw_buf.acct_ctrl |= ACB_DISABLED; 330 return &pw_buf; 331 } 332 333 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) { 334 DEBUG(0, ("getsmbfilepwent: malformed password entry (passwd too short)\n")); 335 continue; 336 } 337 338 if (p[32] != ':') { 339 DEBUG(0, ("getsmbfilepwent: malformed password entry (no terminating :)\n")); 340 continue; 341 } 342 343 if (!strncasecmp((char *) p, "NO PASSWORD", 11)) { 344 pw_buf.smb_passwd = NULL; 345 pw_buf.acct_ctrl |= ACB_PWNOTREQ; 346 } else { 347 if (!pdb_gethexpwd((char *)p, smbpwd)) { 348 DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry (non hex chars)\n")); 349 continue; 350 } 351 pw_buf.smb_passwd = smbpwd; 352 } 353 354 /* 355 * Now check if the NT compatible password is 356 * available. 357 */ 358 pw_buf.smb_nt_passwd = NULL; 359 360 p += 33; /* Move to the first character of the line after 361 the lanman password. */ 362 if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) { 363 if (*p != '*' && *p != 'X') { 364 if(pdb_gethexpwd((char *)p,smbntpwd)) 365 pw_buf.smb_nt_passwd = smbntpwd; 366 } 367 p += 33; /* Move to the first character of the line after 368 the NT password. */ 369 } 370 371 DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n", 372 user_name, uidval)); 373 374 if (*p == '[') 375 { 376 unsigned char *end_p = (unsigned char *)strchr((char *)p, ']'); 377 pw_buf.acct_ctrl = pdb_decode_acct_ctrl((char*)p); 378 379 /* Must have some account type set. */ 380 if(pw_buf.acct_ctrl == 0) 381 pw_buf.acct_ctrl = ACB_NORMAL; 382 383 /* Now try and get the last change time. */ 384 if(end_p) 385 p = end_p + 1; 386 if(*p == ':') { 387 p++; 388 if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) { 389 int i; 390 p += 4; 391 for(i = 0; i < 8; i++) { 392 if(p[i] == '\0' || !isxdigit(p[i])) 393 break; 394 } 395 if(i == 8) { 396 /* 397 * p points at 8 characters of hex digits - 398 * read into a time_t as the seconds since 399 * 1970 that the password was last changed. 400 */ 401 pw_buf.pass_last_set_time = (time_t)strtol((char *)p, NULL, 16); 402 } 403 } 404 } 405 } else { 406 /* 'Old' style file. Fake up based on user name. */ 407 /* 408 * Currently trust accounts are kept in the same 409 * password file as 'normal accounts'. If this changes 410 * we will have to fix this code. JRA. 411 */ 412 if(pw_buf.smb_name[strlen(pw_buf.smb_name) - 1] == '$') { 413 pw_buf.acct_ctrl &= ~ACB_NORMAL; 414 pw_buf.acct_ctrl |= ACB_WSTRUST; 415 } 416 } 417 418 return &pw_buf; 419 } 420 421 DEBUG(5,("getsmbfilepwent: end of file reached.\n")); 422 return NULL; 423} 424 425/************************************************************************* 426 Routine to return the next entry in the smbpasswd list. 427 this function is a nice, messy combination of reading: 428 - the smbpasswd file 429 - the unix password database 430 - smb.conf options (not done at present). 431 *************************************************************************/ 432 433static struct sam_passwd *getsmbfile21pwent(void *vp) 434{ 435 struct smb_passwd *pw_buf = getsmbfilepwent(vp); 436 static struct sam_passwd user; 437 struct passwd *pwfile; 438 439 static pstring full_name; 440 static pstring home_dir; 441 static pstring home_drive; 442 static pstring logon_script; 443 static pstring profile_path; 444 static pstring acct_desc; 445 static pstring workstations; 446 447 DEBUG(5,("getsmbfile21pwent\n")); 448 449 if (pw_buf == NULL) return NULL; 450 451 pwfile = sys_getpwnam(pw_buf->smb_name); 452 if (pwfile == NULL) 453 { 454 DEBUG(0,("getsmbfile21pwent: smbpasswd database is corrupt!\n")); 455 DEBUG(0,("getsmbfile21pwent: username %s not in unix passwd database!\n", pw_buf->smb_name)); 456 return NULL; 457 } 458 459 pdb_init_sam(&user); 460 461 pstrcpy(samlogon_user, pw_buf->smb_name); 462 463 if (samlogon_user[strlen(samlogon_user)-1] != '$') 464 { 465 /* XXXX hack to get standard_sub_basic() to use sam logon username */ 466 /* possibly a better way would be to do a become_user() call */ 467 sam_logon_in_ssb = True; 468 469 user.smb_userid = pw_buf->smb_userid; 470 user.smb_grpid = pwfile->pw_gid; 471 472 user.user_rid = pdb_uid_to_user_rid (user.smb_userid); 473 user.group_rid = pdb_gid_to_group_rid(user.smb_grpid ); 474 475 pstrcpy(full_name , pwfile->pw_gecos ); 476 pstrcpy(logon_script , lp_logon_script ()); 477 pstrcpy(profile_path , lp_logon_path ()); 478 pstrcpy(home_drive , lp_logon_drive ()); 479 pstrcpy(home_dir , lp_logon_home ()); 480 pstrcpy(acct_desc , ""); 481 pstrcpy(workstations , ""); 482 483 sam_logon_in_ssb = False; 484 } 485 else 486 { 487 user.smb_userid = pw_buf->smb_userid; 488 user.smb_grpid = pwfile->pw_gid; 489 490 user.user_rid = pdb_uid_to_user_rid (user.smb_userid); 491 user.group_rid = DOMAIN_GROUP_RID_USERS; /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */ 492 493 pstrcpy(full_name , ""); 494 pstrcpy(logon_script , ""); 495 pstrcpy(profile_path , ""); 496 pstrcpy(home_drive , ""); 497 pstrcpy(home_dir , ""); 498 pstrcpy(acct_desc , ""); 499 pstrcpy(workstations , ""); 500 } 501 502 user.smb_name = pw_buf->smb_name; 503 user.full_name = full_name; 504 user.home_dir = home_dir; 505 user.dir_drive = home_drive; 506 user.logon_script = logon_script; 507 user.profile_path = profile_path; 508 user.acct_desc = acct_desc; 509 user.workstations = workstations; 510 511 user.unknown_str = NULL; /* don't know, yet! */ 512 user.munged_dial = NULL; /* "munged" dial-back telephone number */ 513 514 user.smb_nt_passwd = pw_buf->smb_nt_passwd; 515 user.smb_passwd = pw_buf->smb_passwd; 516 517 user.acct_ctrl = pw_buf->acct_ctrl; 518 519 user.unknown_3 = 0xffffff; /* don't know */ 520 user.logon_divs = 168; /* hours per week */ 521 user.hours_len = 21; /* 21 times 8 bits = 168 */ 522 memset(user.hours, 0xff, user.hours_len); /* available at all hours */ 523 user.unknown_5 = 0x00020000; /* don't know */ 524 user.unknown_5 = 0x000004ec; /* don't know */ 525 526 return &user; 527} 528 529/************************************************************************* 530 Return the current position in the smbpasswd list as an SMB_BIG_UINT. 531 This must be treated as an opaque token. 532*************************************************************************/ 533 534static SMB_BIG_UINT getsmbfilepwpos(void *vp) 535{ 536 return (SMB_BIG_UINT)sys_ftell((FILE *)vp); 537} 538 539/************************************************************************* 540 Set the current position in the smbpasswd list from an SMB_BIG_UINT. 541 This must be treated as an opaque token. 542*************************************************************************/ 543 544static BOOL setsmbfilepwpos(void *vp, SMB_BIG_UINT tok) 545{ 546 return !sys_fseek((FILE *)vp, (SMB_OFF_T)tok, SEEK_SET); 547} 548 549/************************************************************************ 550 Create a new smbpasswd entry - malloced space returned. 551*************************************************************************/ 552 553char *format_new_smbpasswd_entry(struct smb_passwd *newpwd) 554{ 555 int new_entry_length; 556 char *new_entry; 557 char *p; 558 int i; 559 560 new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2; 561 562 if((new_entry = (char *)malloc( new_entry_length )) == NULL) { 563 DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n", newpwd->smb_name )); 564 return NULL; 565 } 566 567 slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid); 568 p = &new_entry[strlen(new_entry)]; 569 570 if(newpwd->smb_passwd != NULL) { 571 for( i = 0; i < 16; i++) { 572 slprintf((char *)&p[i*2], new_entry_length - (p - new_entry) - 1, "%02X", newpwd->smb_passwd[i]); 573 } 574 } else { 575 i=0; 576 if(newpwd->acct_ctrl & ACB_PWNOTREQ) 577 safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry)); 578 else 579 safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry)); 580 } 581 582 p += 32; 583 584 *p++ = ':'; 585 586 if(newpwd->smb_nt_passwd != NULL) { 587 for( i = 0; i < 16; i++) { 588 slprintf((char *)&p[i*2], new_entry_length - 1 - (p - new_entry), "%02X", newpwd->smb_nt_passwd[i]); 589 } 590 } else { 591 if(newpwd->acct_ctrl & ACB_PWNOTREQ) 592 safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry)); 593 else 594 safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry)); 595 } 596 597 p += 32; 598 599 *p++ = ':'; 600 601 /* Add the account encoding and the last change time. */ 602 slprintf((char *)p, new_entry_length - 1 - (p - new_entry), "%s:LCT-%08X:\n", 603 pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN), 604 (uint32)newpwd->pass_last_set_time); 605 606 return new_entry; 607} 608 609/************************************************************************ 610 Routine to add an entry to the smbpasswd file. 611*************************************************************************/ 612 613static BOOL add_smbfilepwd_entry(struct smb_passwd *newpwd) 614{ 615 char *pfile = lp_smb_passwd_file(); 616 struct smb_passwd *pwd = NULL; 617 FILE *fp = NULL; 618 int wr_len; 619 int fd; 620 size_t new_entry_length; 621 char *new_entry; 622 SMB_OFF_T offpos; 623 624 /* Open the smbpassword file - for update. */ 625 fp = startsmbfilepwent(True); 626 627 if (fp == NULL) { 628 DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n")); 629 return False; 630 } 631 632 /* 633 * Scan the file, a line at a time and check if the name matches. 634 */ 635 636 while ((pwd = getsmbfilepwent(fp)) != NULL) { 637 if (strequal(newpwd->smb_name, pwd->smb_name)) { 638 DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name)); 639 endsmbfilepwent(fp); 640 return False; 641 } 642 } 643 644 /* Ok - entry doesn't exist. We can add it */ 645 646 /* Create a new smb passwd entry and set it to the given password. */ 647 /* 648 * The add user write needs to be atomic - so get the fd from 649 * the fp and do a raw write() call. 650 */ 651 fd = fileno(fp); 652 653 if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) { 654 DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \ 655Error was %s\n", newpwd->smb_name, pfile, strerror(errno))); 656 endsmbfilepwent(fp); 657 return False; 658 } 659 660 if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) { 661 DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \ 662Error was %s\n", newpwd->smb_name, pfile, strerror(errno))); 663 endsmbfilepwent(fp); 664 return False; 665 } 666 667 new_entry_length = strlen(new_entry); 668 669#ifdef DEBUG_PASSWORD 670 DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|", 671 fd, new_entry_length, new_entry)); 672#endif 673 674 if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) { 675 DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \ 676Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno))); 677 678 /* Remove the entry we just wrote. */ 679 if(sys_ftruncate(fd, offpos) == -1) { 680 DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \ 681Error was %s. Password file may be corrupt ! Please examine by hand !\n", 682 newpwd->smb_name, strerror(errno))); 683 } 684 685 endsmbfilepwent(fp); 686 free(new_entry); 687 return False; 688 } 689 690 free(new_entry); 691 endsmbfilepwent(fp); 692 return True; 693} 694 695/************************************************************************ 696 Routine to search the smbpasswd file for an entry matching the username. 697 and then modify its password entry. We can't use the startsmbpwent()/ 698 getsmbpwent()/endsmbpwent() interfaces here as we depend on looking 699 in the actual file to decide how much room we have to write data. 700 override = False, normal 701 override = True, override XXXXXXXX'd out password or NO PASS 702************************************************************************/ 703 704static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override) 705{ 706 /* Static buffers we will return. */ 707 static pstring user_name; 708 709 char linebuf[256]; 710 char readbuf[1024]; 711 unsigned char c; 712 fstring ascii_p16; 713 fstring encode_bits; 714 unsigned char *p = NULL; 715 size_t linebuf_len = 0; 716 FILE *fp; 717 int lockfd; 718 char *pfile = lp_smb_passwd_file(); 719 BOOL found_entry = False; 720 BOOL got_pass_last_set_time = False; 721 722 SMB_OFF_T pwd_seekpos = 0; 723 724 int i; 725 int wr_len; 726 int fd; 727 728 if (!*pfile) { 729 DEBUG(0, ("No SMB password file set\n")); 730 return False; 731 } 732 DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile)); 733 734 fp = sys_fopen(pfile, "r+"); 735 736 if (fp == NULL) { 737 DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile)); 738 return False; 739 } 740 /* Set a buffer to do more efficient reads */ 741 setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf)); 742 743 lockfd = fileno(fp); 744 745 if (!pw_file_lock(lockfd, F_WRLCK, 5, &pw_file_lock_depth)) { 746 DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile)); 747 fclose(fp); 748 return False; 749 } 750 751 /* Make sure it is only rw by the owner */ 752 chmod(pfile, 0600); 753 754 /* We have a write lock on the file. */ 755 /* 756 * Scan the file, a line at a time and check if the name matches. 757 */ 758 while (!feof(fp)) { 759 pwd_seekpos = sys_ftell(fp); 760 761 linebuf[0] = '\0'; 762 763 fgets(linebuf, sizeof(linebuf), fp); 764 if (ferror(fp)) { 765 pw_file_unlock(lockfd, &pw_file_lock_depth); 766 fclose(fp); 767 return False; 768 } 769 770 /* 771 * Check if the string is terminated with a newline - if not 772 * then we must keep reading and discard until we get one. 773 */ 774 linebuf_len = strlen(linebuf); 775 if (linebuf[linebuf_len - 1] != '\n') { 776 c = '\0'; 777 while (!ferror(fp) && !feof(fp)) { 778 c = fgetc(fp); 779 if (c == '\n') { 780 break; 781 } 782 } 783 } else { 784 linebuf[linebuf_len - 1] = '\0'; 785 } 786 787#ifdef DEBUG_PASSWORD 788 DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf)); 789#endif 790 791 if ((linebuf[0] == 0) && feof(fp)) { 792 DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n")); 793 break; 794 } 795 796 /* 797 * The line we have should be of the form :- 798 * 799 * username:uid:[32hex bytes]:....other flags presently 800 * ignored.... 801 * 802 * or, 803 * 804 * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored. 805 * 806 * if Windows NT compatible passwords are also present. 807 */ 808 809 if (linebuf[0] == '#' || linebuf[0] == '\0') { 810 DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n")); 811 continue; 812 } 813 814 p = (unsigned char *) strchr(linebuf, ':'); 815 816 if (p == NULL) { 817 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n")); 818 continue; 819 } 820 821 /* 822 * As 256 is shorter than a pstring we don't need to check 823 * length here - if this ever changes.... 824 */ 825 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf)); 826 user_name[PTR_DIFF(p, linebuf)] = '\0'; 827 if (strequal(user_name, pwd->smb_name)) { 828 found_entry = True; 829 break; 830 } 831 } 832 833 if (!found_entry) { 834 pw_file_unlock(lockfd, &pw_file_lock_depth); 835 fclose(fp); 836 return False; 837 } 838 839 DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n")); 840 841 /* User name matches - get uid and password */ 842 p++; /* Go past ':' */ 843 844 if (!isdigit(*p)) { 845 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n")); 846 pw_file_unlock(lockfd, &pw_file_lock_depth); 847 fclose(fp); 848 return False; 849 } 850 851 while (*p && isdigit(*p)) 852 p++; 853 if (*p != ':') { 854 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n")); 855 pw_file_unlock(lockfd, &pw_file_lock_depth); 856 fclose(fp); 857 return False; 858 } 859 860 /* 861 * Now get the password value - this should be 32 hex digits 862 * which are the ascii representations of a 16 byte string. 863 * Get two at a time and put them into the password. 864 */ 865 p++; 866 867 /* Record exact password position */ 868 pwd_seekpos += PTR_DIFF(p, linebuf); 869 870 if (!override && (*p == '*' || *p == 'X')) { 871 /* Password deliberately invalid - end here. */ 872 DEBUG(10, ("mod_smbfilepwd_entry: entry invalidated for user %s\n", user_name)); 873 pw_file_unlock(lockfd, &pw_file_lock_depth); 874 fclose(fp); 875 return False; 876 } 877 878 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) { 879 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n")); 880 pw_file_unlock(lockfd,&pw_file_lock_depth); 881 fclose(fp); 882 return (False); 883 } 884 885 if (p[32] != ':') { 886 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n")); 887 pw_file_unlock(lockfd,&pw_file_lock_depth); 888 fclose(fp); 889 return False; 890 } 891 892 if (!override && (*p == '*' || *p == 'X')) { 893 pw_file_unlock(lockfd,&pw_file_lock_depth); 894 fclose(fp); 895 return False; 896 } 897 898 /* Now check if the NT compatible password is 899 available. */ 900 p += 33; /* Move to the first character of the line after 901 the lanman password. */ 902 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) { 903 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n")); 904 pw_file_unlock(lockfd,&pw_file_lock_depth); 905 fclose(fp); 906 return (False); 907 } 908 909 if (p[32] != ':') { 910 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n")); 911 pw_file_unlock(lockfd,&pw_file_lock_depth); 912 fclose(fp); 913 return False; 914 } 915 916 /* 917 * Now check if the account info and the password last 918 * change time is available. 919 */ 920 p += 33; /* Move to the first character of the line after 921 the NT password. */ 922 923 /* 924 * If both NT and lanman passwords are provided - reset password 925 * not required flag. 926 */ 927 928 if(pwd->smb_passwd != NULL || pwd->smb_nt_passwd != NULL) { 929 /* Reqiure password in the future (should ACB_DISABLED also be reset?) */ 930 pwd->acct_ctrl &= ~(ACB_PWNOTREQ); 931 } 932 933 if (*p == '[') { 934 935 i = 0; 936 encode_bits[i++] = *p++; 937 while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']')) 938 encode_bits[i++] = *p++; 939 940 encode_bits[i++] = ']'; 941 encode_bits[i++] = '\0'; 942 943 if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) { 944 /* 945 * We are using a new format, space padded 946 * acct ctrl field. Encode the given acct ctrl 947 * bits into it. 948 */ 949 fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN)); 950 } else { 951 /* 952 * If using the old format and the ACB_DISABLED or 953 * ACB_PWNOTREQ are set then set the lanman and NT passwords to NULL 954 * here as we have no space to encode the change. 955 */ 956 if(pwd->acct_ctrl & (ACB_DISABLED|ACB_PWNOTREQ)) { 957 pwd->smb_passwd = NULL; 958 pwd->smb_nt_passwd = NULL; 959 } 960 } 961 962 /* Go past the ']' */ 963 if(linebuf_len > PTR_DIFF(p, linebuf)) 964 p++; 965 966 if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) { 967 p++; 968 969 /* We should be pointing at the LCT entry. */ 970 if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) { 971 972 p += 4; 973 for(i = 0; i < 8; i++) { 974 if(p[i] == '\0' || !isxdigit(p[i])) 975 break; 976 } 977 if(i == 8) { 978 /* 979 * p points at 8 characters of hex digits - 980 * read into a time_t as the seconds since 981 * 1970 that the password was last changed. 982 */ 983 got_pass_last_set_time = True; 984 } /* i == 8 */ 985 } /* *p && StrnCaseCmp() */ 986 } /* p == ':' */ 987 } /* p == '[' */ 988 989 /* Entry is correctly formed. */ 990 991 /* Create the 32 byte representation of the new p16 */ 992 if(pwd->smb_passwd != NULL) { 993 for (i = 0; i < 16; i++) { 994 slprintf(&ascii_p16[i*2], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_passwd[i]); 995 } 996 } else { 997 if(pwd->acct_ctrl & ACB_PWNOTREQ) 998 fstrcpy(ascii_p16, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX"); 999 else 1000 fstrcpy(ascii_p16, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); 1001 } 1002 1003 /* Add on the NT md4 hash */ 1004 ascii_p16[32] = ':'; 1005 wr_len = 66; 1006 if (pwd->smb_nt_passwd != NULL) { 1007 for (i = 0; i < 16; i++) { 1008 slprintf(&ascii_p16[(i*2)+33], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_nt_passwd[i]); 1009 } 1010 } else { 1011 if(pwd->acct_ctrl & ACB_PWNOTREQ) 1012 fstrcpy(&ascii_p16[33], "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX"); 1013 else 1014 fstrcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); 1015 } 1016 ascii_p16[65] = ':'; 1017 ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */ 1018 1019 /* Add on the account info bits and the time of last 1020 password change. */ 1021 1022 pwd->pass_last_set_time = time(NULL); 1023 1024 if(got_pass_last_set_time) { 1025 slprintf(&ascii_p16[strlen(ascii_p16)], 1026 sizeof(ascii_p16)-(strlen(ascii_p16)+1), 1027 "%s:LCT-%08X:", 1028 encode_bits, (uint32)pwd->pass_last_set_time ); 1029 wr_len = strlen(ascii_p16); 1030 } 1031 1032#ifdef DEBUG_PASSWORD 1033 DEBUG(100,("mod_smbfilepwd_entry: ")); 1034 dump_data(100, ascii_p16, wr_len); 1035#endif 1036 1037 if(wr_len > sizeof(linebuf)) { 1038 DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1)); 1039 pw_file_unlock(lockfd,&pw_file_lock_depth); 1040 fclose(fp); 1041 return (False); 1042 } 1043 1044 /* 1045 * Do an atomic write into the file at the position defined by 1046 * seekpos. 1047 */ 1048 1049 /* The mod user write needs to be atomic - so get the fd from 1050 the fp and do a raw write() call. 1051 */ 1052 1053 fd = fileno(fp); 1054 1055 if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) { 1056 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile)); 1057 pw_file_unlock(lockfd,&pw_file_lock_depth); 1058 fclose(fp); 1059 return False; 1060 } 1061 1062 /* Sanity check - ensure the areas we are writing are framed by ':' */ 1063 if (read(fd, linebuf, wr_len+1) != wr_len+1) { 1064 DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile)); 1065 pw_file_unlock(lockfd,&pw_file_lock_depth); 1066 fclose(fp); 1067 return False; 1068 } 1069 1070 if ((linebuf[0] != ':') || (linebuf[wr_len] != ':')) { 1071 DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile)); 1072 pw_file_unlock(lockfd,&pw_file_lock_depth); 1073 fclose(fp); 1074 return False; 1075 } 1076 1077 if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) { 1078 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile)); 1079 pw_file_unlock(lockfd,&pw_file_lock_depth); 1080 fclose(fp); 1081 return False; 1082 } 1083 1084 if (write(fd, ascii_p16, wr_len) != wr_len) { 1085 DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile)); 1086 pw_file_unlock(lockfd,&pw_file_lock_depth); 1087 fclose(fp); 1088 return False; 1089 } 1090 1091 pw_file_unlock(lockfd,&pw_file_lock_depth); 1092 fclose(fp); 1093 return True; 1094} 1095 1096/************************************************************************ 1097 Routine to delete an entry in the smbpasswd file by name. 1098*************************************************************************/ 1099 1100static BOOL del_smbfilepwd_entry(const char *name) 1101{ 1102 char *pfile = lp_smb_passwd_file(); 1103 pstring pfile2; 1104 struct smb_passwd *pwd = NULL; 1105 FILE *fp = NULL; 1106 FILE *fp_write = NULL; 1107 int pfile2_lockdepth = 0; 1108 1109 slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)getpid() ); 1110 1111 /* 1112 * Open the smbpassword file - for update. It needs to be update 1113 * as we need any other processes to wait until we have replaced 1114 * it. 1115 */ 1116 1117 if((fp = startsmbfilepwent(True)) == NULL) { 1118 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile)); 1119 return False; 1120 } 1121 1122 /* 1123 * Create the replacement password file. 1124 */ 1125 if((fp_write = startsmbfilepwent_internal(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) { 1126 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile)); 1127 endsmbfilepwent(fp); 1128 return False; 1129 } 1130 1131 /* 1132 * Scan the file, a line at a time and check if the name matches. 1133 */ 1134 1135 while ((pwd = getsmbfilepwent(fp)) != NULL) { 1136 char *new_entry; 1137 size_t new_entry_length; 1138 1139 if (strequal(name, pwd->smb_name)) { 1140 DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name)); 1141 continue; 1142 } 1143 1144 /* 1145 * We need to copy the entry out into the second file. 1146 */ 1147 1148 if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) { 1149 DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \ 1150Error was %s\n", pwd->smb_name, pfile2, strerror(errno))); 1151 unlink(pfile2); 1152 endsmbfilepwent(fp); 1153 endsmbfilepwent_internal(fp_write,&pfile2_lockdepth); 1154 return False; 1155 } 1156 1157 new_entry_length = strlen(new_entry); 1158 1159 if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) { 1160 DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \ 1161Error was %s\n", pwd->smb_name, pfile2, strerror(errno))); 1162 unlink(pfile2); 1163 endsmbfilepwent(fp); 1164 endsmbfilepwent_internal(fp_write,&pfile2_lockdepth); 1165 free(new_entry); 1166 return False; 1167 } 1168 1169 free(new_entry); 1170 } 1171 1172 /* 1173 * Ensure pfile2 is flushed before rename. 1174 */ 1175 1176 if(fflush(fp_write) != 0) { 1177 DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno))); 1178 endsmbfilepwent(fp); 1179 endsmbfilepwent_internal(fp_write,&pfile2_lockdepth); 1180 return False; 1181 } 1182 1183 /* 1184 * Do an atomic rename - then release the locks. 1185 */ 1186 1187 if(rename(pfile2,pfile) != 0) { 1188 unlink(pfile2); 1189 } 1190 endsmbfilepwent(fp); 1191 endsmbfilepwent_internal(fp_write,&pfile2_lockdepth); 1192 return True; 1193} 1194 1195/* 1196 * Stub functions - implemented in terms of others. 1197 */ 1198 1199static BOOL mod_smbfile21pwd_entry(struct sam_passwd* pwd, BOOL override) 1200{ 1201 return mod_smbfilepwd_entry(pdb_sam_to_smb(pwd), override); 1202} 1203 1204static BOOL add_smbfile21pwd_entry(struct sam_passwd *newpwd) 1205{ 1206 return add_smbfilepwd_entry(pdb_sam_to_smb(newpwd)); 1207} 1208 1209static struct sam_disp_info *getsmbfiledispnam(char *name) 1210{ 1211 return pdb_sam_to_dispinfo(getsam21pwnam(name)); 1212} 1213 1214static struct sam_disp_info *getsmbfiledisprid(uint32 rid) 1215{ 1216 return pdb_sam_to_dispinfo(getsam21pwrid(rid)); 1217} 1218 1219static struct sam_disp_info *getsmbfiledispent(void *vp) 1220{ 1221 return pdb_sam_to_dispinfo(getsam21pwent(vp)); 1222} 1223 1224static struct passdb_ops file_ops = { 1225 startsmbfilepwent, 1226 endsmbfilepwent, 1227 getsmbfilepwpos, 1228 setsmbfilepwpos, 1229 iterate_getsmbpwnam, /* In passdb.c */ 1230 iterate_getsmbpwuid, /* In passdb.c */ 1231 iterate_getsmbpwrid, /* In passdb.c */ 1232 getsmbfilepwent, 1233 add_smbfilepwd_entry, 1234 mod_smbfilepwd_entry, 1235 del_smbfilepwd_entry, 1236 getsmbfile21pwent, 1237 iterate_getsam21pwnam, 1238 iterate_getsam21pwuid, 1239 iterate_getsam21pwrid, 1240 add_smbfile21pwd_entry, 1241 mod_smbfile21pwd_entry, 1242 getsmbfiledispnam, 1243 getsmbfiledisprid, 1244 getsmbfiledispent 1245}; 1246 1247struct passdb_ops *file_initialize_password_db(void) 1248{ 1249 return &file_ops; 1250} 1251 1252#else 1253 /* Do *NOT* make this function static. It breaks the compile on gcc. JRA */ 1254 void smbpass_dummy_function(void) { } /* stop some compilers complaining */ 1255#endif /* USE_SMBPASS_DB */ 1256