Fetch authentication info before actually using it.
[Samba.git] / source / passdb / smbpass.c
blobec1a984b76bbd716dfd5989ca62ded286e7bcf89
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.
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.
20 #include "includes.h"
22 #ifdef USE_SMBPASS_DB
24 extern int DEBUGLEVEL;
25 extern pstring samlogon_user;
26 extern BOOL sam_logon_in_ssb;
28 static int pw_file_lock_depth;
30 enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE };
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 ****************************************************************/
39 static void *startsmbfilepwent_internal(const char *pfile, enum pwf_access_type type, int *lock_depth)
41 FILE *fp = NULL;
42 const char *open_mode = NULL;
43 int race_loop = 0;
44 int lock_type = F_RDLCK;
46 if (!*pfile) {
47 DEBUG(0, ("startsmbfilepwent: No SMB password file set\n"));
48 return (NULL);
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:
62 * Ensure atomic file creation.
65 int i, fd = -1;
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... */
72 if(fd == -1) {
73 DEBUG(0,("startsmbfilepwent_internal: too many race conditions creating file %s\n", pfile));
74 return NULL;
76 close(fd);
77 open_mode = "r+b";
78 lock_type = F_WRLCK;
79 break;
83 for(race_loop = 0; race_loop < 5; race_loop++) {
84 DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile));
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;
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;
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.
102 if(type == PWF_READ) {
103 break;
104 } else {
105 SMB_STRUCT_STAT sbuf1, sbuf2;
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.
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;
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;
129 if( sbuf1.st_ino == sbuf2.st_ino) {
130 /* No race. */
131 break;
135 * Race occurred - back off and try again...
138 pw_file_unlock(fileno(fp), lock_depth);
139 fclose(fp);
143 if(race_loop == 5) {
144 DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile));
145 return NULL;
148 /* Set a buffer to do more efficient reads */
149 setvbuf(fp, (char *)NULL, _IOFBF, 1024);
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. \
154 Error was %s\n.", pfile, strerror(errno) ));
155 pw_file_unlock(fileno(fp), lock_depth);
156 fclose(fp);
157 return NULL;
160 /* We have a lock on the file. */
161 return (void *)fp;
164 /***************************************************************
165 Start to enumerate the smbpasswd list. Returns a void pointer
166 to ensure no modification outside this module.
167 ****************************************************************/
169 static void *startsmbfilepwent(BOOL update)
171 return startsmbfilepwent_internal(lp_smb_passwd_file(), update ? PWF_UPDATE : PWF_READ, &pw_file_lock_depth);
174 /***************************************************************
175 End enumeration of the smbpasswd list.
176 ****************************************************************/
178 static void endsmbfilepwent_internal(void *vp, int *lock_depth)
180 FILE *fp = (FILE *)vp;
182 pw_file_unlock(fileno(fp), lock_depth);
183 fclose(fp);
184 DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n"));
187 /***************************************************************
188 End enumeration of the smbpasswd list - operate on the default
189 lock_depth.
190 ****************************************************************/
192 static void endsmbfilepwent(void *vp)
194 endsmbfilepwent_internal(vp, &pw_file_lock_depth);
197 /*************************************************************************
198 Routine to return the next entry in the smbpasswd list.
199 *************************************************************************/
201 static struct smb_passwd *getsmbfilepwent(void *vp)
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;
215 if(fp == NULL) {
216 DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
217 return NULL;
220 pdb_init_smb(&pw_buf);
222 pw_buf.acct_ctrl = ACB_NORMAL;
225 * Scan the file, a line at a time and check if the name matches.
227 while (!feof(fp)) {
228 linebuf[0] = '\0';
230 fgets(linebuf, 256, fp);
231 if (ferror(fp)) {
232 return NULL;
236 * Check if the string is terminated with a newline - if not
237 * then we must keep reading and discard until we get one.
239 if ((linebuf_len = strlen(linebuf)) == 0)
240 continue;
242 if (linebuf[linebuf_len - 1] != '\n') {
243 c = '\0';
244 while (!ferror(fp) && !feof(fp)) {
245 c = fgetc(fp);
246 if (c == '\n')
247 break;
249 } else
250 linebuf[linebuf_len - 1] = '\0';
252 #ifdef DEBUG_PASSWORD
253 DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
254 #endif
255 if ((linebuf[0] == 0) && feof(fp)) {
256 DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
257 break;
260 * The line we have should be of the form :-
262 * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
263 * ignored....
265 * or,
267 * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
269 * if Windows NT compatible passwords are also present.
270 * [Account type] is an ascii encoding of the type of account.
271 * LCT-(8 hex digits) is the time_t value of the last change time.
274 if (linebuf[0] == '#' || linebuf[0] == '\0') {
275 DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
276 continue;
278 p = (unsigned char *) strchr(linebuf, ':');
279 if (p == NULL) {
280 DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
281 continue;
284 * As 256 is shorter than a pstring we don't need to check
285 * length here - if this ever changes....
287 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
288 user_name[PTR_DIFF(p, linebuf)] = '\0';
290 /* Get smb uid. */
292 p++; /* Go past ':' */
294 if(*p == '-') {
295 DEBUG(0, ("getsmbfilepwent: uids in the smbpasswd file must not be negative.\n"));
296 continue;
299 if (!isdigit(*p)) {
300 DEBUG(0, ("getsmbfilepwent: malformed password entry (uid not number)\n"));
301 continue;
304 uidval = atoi((char *) p);
306 while (*p && isdigit(*p))
307 p++;
309 if (*p != ':') {
310 DEBUG(0, ("getsmbfilepwent: malformed password entry (no : after uid)\n"));
311 continue;
314 pw_buf.smb_name = user_name;
315 pw_buf.smb_userid = uidval;
318 * Now get the password value - this should be 32 hex digits
319 * which are the ascii representations of a 16 byte string.
320 * Get two at a time and put them into the password.
323 /* Skip the ':' */
324 p++;
326 if (*p == '*' || *p == 'X') {
327 /* Password deliberately invalid - end here. */
328 DEBUG(10, ("getsmbfilepwent: entry invalidated for user %s\n", user_name));
329 pw_buf.smb_nt_passwd = NULL;
330 pw_buf.smb_passwd = NULL;
331 pw_buf.acct_ctrl |= ACB_DISABLED;
332 return &pw_buf;
335 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
336 DEBUG(0, ("getsmbfilepwent: malformed password entry (passwd too short)\n"));
337 continue;
340 if (p[32] != ':') {
341 DEBUG(0, ("getsmbfilepwent: malformed password entry (no terminating :)\n"));
342 continue;
345 if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
346 pw_buf.smb_passwd = NULL;
347 pw_buf.acct_ctrl |= ACB_PWNOTREQ;
348 } else {
349 if (!pdb_gethexpwd((char *)p, smbpwd)) {
350 DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry (non hex chars)\n"));
351 continue;
353 pw_buf.smb_passwd = smbpwd;
357 * Now check if the NT compatible password is
358 * available.
360 pw_buf.smb_nt_passwd = NULL;
362 p += 33; /* Move to the first character of the line after
363 the lanman password. */
364 if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
365 if (*p != '*' && *p != 'X') {
366 if(pdb_gethexpwd((char *)p,smbntpwd))
367 pw_buf.smb_nt_passwd = smbntpwd;
369 p += 33; /* Move to the first character of the line after
370 the NT password. */
373 DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
374 user_name, uidval));
376 if (*p == '[')
378 unsigned char *end_p = (unsigned char *)strchr((char *)p, ']');
379 pw_buf.acct_ctrl = pdb_decode_acct_ctrl((char*)p);
381 /* Must have some account type set. */
382 if(pw_buf.acct_ctrl == 0)
383 pw_buf.acct_ctrl = ACB_NORMAL;
385 /* Now try and get the last change time. */
386 if(end_p)
387 p = end_p + 1;
388 if(*p == ':') {
389 p++;
390 if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
391 int i;
392 p += 4;
393 for(i = 0; i < 8; i++) {
394 if(p[i] == '\0' || !isxdigit(p[i]))
395 break;
397 if(i == 8) {
399 * p points at 8 characters of hex digits -
400 * read into a time_t as the seconds since
401 * 1970 that the password was last changed.
403 pw_buf.pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
407 } else {
408 /* 'Old' style file. Fake up based on user name. */
410 * Currently trust accounts are kept in the same
411 * password file as 'normal accounts'. If this changes
412 * we will have to fix this code. JRA.
414 if(pw_buf.smb_name[strlen(pw_buf.smb_name) - 1] == '$') {
415 pw_buf.acct_ctrl &= ~ACB_NORMAL;
416 pw_buf.acct_ctrl |= ACB_WSTRUST;
420 return &pw_buf;
423 DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
424 return NULL;
427 /*************************************************************************
428 Routine to return the next entry in the smbpasswd list.
429 this function is a nice, messy combination of reading:
430 - the smbpasswd file
431 - the unix password database
432 - smb.conf options (not done at present).
433 *************************************************************************/
435 static struct sam_passwd *getsmbfile21pwent(void *vp)
437 struct smb_passwd *pw_buf = getsmbfilepwent(vp);
438 static struct sam_passwd user;
439 struct passwd *pwfile;
441 static pstring full_name;
442 static pstring home_dir;
443 static pstring home_drive;
444 static pstring logon_script;
445 static pstring profile_path;
446 static pstring acct_desc;
447 static pstring workstations;
449 DEBUG(5,("getsmbfile21pwent\n"));
451 if (pw_buf == NULL) return NULL;
453 pwfile = sys_getpwnam(pw_buf->smb_name);
454 if (pwfile == NULL)
456 DEBUG(0,("getsmbfile21pwent: smbpasswd database is corrupt!\n"));
457 DEBUG(0,("getsmbfile21pwent: username %s not in unix passwd database!\n", pw_buf->smb_name));
458 return NULL;
461 pdb_init_sam(&user);
463 pstrcpy(samlogon_user, pw_buf->smb_name);
465 if (samlogon_user[strlen(samlogon_user)-1] != '$')
467 /* XXXX hack to get standard_sub_basic() to use sam logon username */
468 /* possibly a better way would be to do a become_user() call */
469 sam_logon_in_ssb = True;
471 user.smb_userid = pw_buf->smb_userid;
472 user.smb_grpid = pwfile->pw_gid;
474 user.user_rid = pdb_uid_to_user_rid (user.smb_userid);
475 user.group_rid = pdb_gid_to_group_rid(user.smb_grpid );
477 pstrcpy(full_name , pwfile->pw_gecos );
478 pstrcpy(logon_script , lp_logon_script ());
479 pstrcpy(profile_path , lp_logon_path ());
480 pstrcpy(home_drive , lp_logon_drive ());
481 pstrcpy(home_dir , lp_logon_home ());
482 pstrcpy(acct_desc , "");
483 pstrcpy(workstations , "");
485 sam_logon_in_ssb = False;
487 else
489 user.smb_userid = pw_buf->smb_userid;
490 user.smb_grpid = pwfile->pw_gid;
492 user.user_rid = pdb_uid_to_user_rid (user.smb_userid);
493 user.group_rid = DOMAIN_GROUP_RID_USERS; /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */
495 pstrcpy(full_name , "");
496 pstrcpy(logon_script , "");
497 pstrcpy(profile_path , "");
498 pstrcpy(home_drive , "");
499 pstrcpy(home_dir , "");
500 pstrcpy(acct_desc , "");
501 pstrcpy(workstations , "");
504 user.smb_name = pw_buf->smb_name;
505 user.full_name = full_name;
506 user.home_dir = home_dir;
507 user.dir_drive = home_drive;
508 user.logon_script = logon_script;
509 user.profile_path = profile_path;
510 user.acct_desc = acct_desc;
511 user.workstations = workstations;
513 user.unknown_str = NULL; /* don't know, yet! */
514 user.munged_dial = NULL; /* "munged" dial-back telephone number */
516 user.smb_nt_passwd = pw_buf->smb_nt_passwd;
517 user.smb_passwd = pw_buf->smb_passwd;
519 user.acct_ctrl = pw_buf->acct_ctrl;
521 user.unknown_3 = 0xffffff; /* don't know */
522 user.logon_divs = 168; /* hours per week */
523 user.hours_len = 21; /* 21 times 8 bits = 168 */
524 memset(user.hours, 0xff, user.hours_len); /* available at all hours */
525 user.unknown_5 = 0x00020000; /* don't know */
526 user.unknown_5 = 0x000004ec; /* don't know */
528 return &user;
531 /*************************************************************************
532 Return the current position in the smbpasswd list as an SMB_BIG_UINT.
533 This must be treated as an opaque token.
534 *************************************************************************/
536 static SMB_BIG_UINT getsmbfilepwpos(void *vp)
538 return (SMB_BIG_UINT)sys_ftell((FILE *)vp);
541 /*************************************************************************
542 Set the current position in the smbpasswd list from an SMB_BIG_UINT.
543 This must be treated as an opaque token.
544 *************************************************************************/
546 static BOOL setsmbfilepwpos(void *vp, SMB_BIG_UINT tok)
548 return !sys_fseek((FILE *)vp, (SMB_OFF_T)tok, SEEK_SET);
551 /************************************************************************
552 Create a new smbpasswd entry - malloced space returned.
553 *************************************************************************/
555 char *format_new_smbpasswd_entry(struct smb_passwd *newpwd)
557 int new_entry_length;
558 char *new_entry;
559 char *p;
560 int i;
562 new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
564 if((new_entry = (char *)malloc( new_entry_length )) == NULL) {
565 DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n", newpwd->smb_name ));
566 return NULL;
569 slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
570 p = &new_entry[strlen(new_entry)];
572 if(newpwd->smb_passwd != NULL) {
573 for( i = 0; i < 16; i++) {
574 slprintf((char *)&p[i*2], new_entry_length - (p - new_entry) - 1, "%02X", newpwd->smb_passwd[i]);
576 } else {
577 i=0;
578 if(newpwd->acct_ctrl & ACB_PWNOTREQ)
579 safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
580 else
581 safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
584 p += 32;
586 *p++ = ':';
588 if(newpwd->smb_nt_passwd != NULL) {
589 for( i = 0; i < 16; i++) {
590 slprintf((char *)&p[i*2], new_entry_length - 1 - (p - new_entry), "%02X", newpwd->smb_nt_passwd[i]);
592 } else {
593 if(newpwd->acct_ctrl & ACB_PWNOTREQ)
594 safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
595 else
596 safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
599 p += 32;
601 *p++ = ':';
603 /* Add the account encoding and the last change time. */
604 slprintf((char *)p, new_entry_length - 1 - (p - new_entry), "%s:LCT-%08X:\n",
605 pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
606 (uint32)newpwd->pass_last_set_time);
608 return new_entry;
611 /************************************************************************
612 Routine to add an entry to the smbpasswd file.
613 *************************************************************************/
615 static BOOL add_smbfilepwd_entry(struct smb_passwd *newpwd)
617 char *pfile = lp_smb_passwd_file();
618 struct smb_passwd *pwd = NULL;
619 FILE *fp = NULL;
620 int wr_len;
621 int fd;
622 size_t new_entry_length;
623 char *new_entry;
624 SMB_OFF_T offpos;
626 /* Open the smbpassword file - for update. */
627 fp = startsmbfilepwent(True);
629 if (fp == NULL) {
630 DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
631 return False;
635 * Scan the file, a line at a time and check if the name matches.
638 while ((pwd = getsmbfilepwent(fp)) != NULL) {
639 if (strequal(newpwd->smb_name, pwd->smb_name)) {
640 DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
641 endsmbfilepwent(fp);
642 return False;
646 /* Ok - entry doesn't exist. We can add it */
648 /* Create a new smb passwd entry and set it to the given password. */
650 * The add user write needs to be atomic - so get the fd from
651 * the fp and do a raw write() call.
653 fd = fileno(fp);
655 if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) {
656 DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
657 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
658 endsmbfilepwent(fp);
659 return False;
662 if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) {
663 DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
664 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
665 endsmbfilepwent(fp);
666 return False;
669 new_entry_length = strlen(new_entry);
671 #ifdef DEBUG_PASSWORD
672 DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|",
673 fd, new_entry_length, new_entry));
674 #endif
676 if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) {
677 DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
678 Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
680 /* Remove the entry we just wrote. */
681 if(sys_ftruncate(fd, offpos) == -1) {
682 DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
683 Error was %s. Password file may be corrupt ! Please examine by hand !\n",
684 newpwd->smb_name, strerror(errno)));
687 endsmbfilepwent(fp);
688 free(new_entry);
689 return False;
692 free(new_entry);
693 endsmbfilepwent(fp);
694 return True;
697 /************************************************************************
698 Routine to search the smbpasswd file for an entry matching the username.
699 and then modify its password entry. We can't use the startsmbpwent()/
700 getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
701 in the actual file to decide how much room we have to write data.
702 override = False, normal
703 override = True, override XXXXXXXX'd out password or NO PASS
704 ************************************************************************/
706 static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
708 /* Static buffers we will return. */
709 static pstring user_name;
711 char linebuf[256];
712 char readbuf[1024];
713 unsigned char c;
714 fstring ascii_p16;
715 fstring encode_bits;
716 unsigned char *p = NULL;
717 size_t linebuf_len = 0;
718 FILE *fp;
719 int lockfd;
720 char *pfile = lp_smb_passwd_file();
721 BOOL found_entry = False;
722 BOOL got_pass_last_set_time = False;
724 SMB_OFF_T pwd_seekpos = 0;
726 int i;
727 int wr_len;
728 int fd;
730 if (!*pfile) {
731 DEBUG(0, ("No SMB password file set\n"));
732 return False;
734 DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
736 fp = sys_fopen(pfile, "r+");
738 if (fp == NULL) {
739 DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
740 return False;
742 /* Set a buffer to do more efficient reads */
743 setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
745 lockfd = fileno(fp);
747 if (!pw_file_lock(lockfd, F_WRLCK, 5, &pw_file_lock_depth)) {
748 DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
749 fclose(fp);
750 return False;
753 /* Make sure it is only rw by the owner */
754 chmod(pfile, 0600);
756 /* We have a write lock on the file. */
758 * Scan the file, a line at a time and check if the name matches.
760 while (!feof(fp)) {
761 pwd_seekpos = sys_ftell(fp);
763 linebuf[0] = '\0';
765 fgets(linebuf, sizeof(linebuf), fp);
766 if (ferror(fp)) {
767 pw_file_unlock(lockfd, &pw_file_lock_depth);
768 fclose(fp);
769 return False;
773 * Check if the string is terminated with a newline - if not
774 * then we must keep reading and discard until we get one.
776 linebuf_len = strlen(linebuf);
777 if (linebuf[linebuf_len - 1] != '\n') {
778 c = '\0';
779 while (!ferror(fp) && !feof(fp)) {
780 c = fgetc(fp);
781 if (c == '\n') {
782 break;
785 } else {
786 linebuf[linebuf_len - 1] = '\0';
789 #ifdef DEBUG_PASSWORD
790 DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
791 #endif
793 if ((linebuf[0] == 0) && feof(fp)) {
794 DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
795 break;
799 * The line we have should be of the form :-
801 * username:uid:[32hex bytes]:....other flags presently
802 * ignored....
804 * or,
806 * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
808 * if Windows NT compatible passwords are also present.
811 if (linebuf[0] == '#' || linebuf[0] == '\0') {
812 DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
813 continue;
816 p = (unsigned char *) strchr(linebuf, ':');
818 if (p == NULL) {
819 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
820 continue;
824 * As 256 is shorter than a pstring we don't need to check
825 * length here - if this ever changes....
827 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
828 user_name[PTR_DIFF(p, linebuf)] = '\0';
829 if (strequal(user_name, pwd->smb_name)) {
830 found_entry = True;
831 break;
835 if (!found_entry) {
836 pw_file_unlock(lockfd, &pw_file_lock_depth);
837 fclose(fp);
838 return False;
841 DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n"));
843 /* User name matches - get uid and password */
844 p++; /* Go past ':' */
846 if (!isdigit(*p)) {
847 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n"));
848 pw_file_unlock(lockfd, &pw_file_lock_depth);
849 fclose(fp);
850 return False;
853 while (*p && isdigit(*p))
854 p++;
855 if (*p != ':') {
856 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n"));
857 pw_file_unlock(lockfd, &pw_file_lock_depth);
858 fclose(fp);
859 return False;
863 * Now get the password value - this should be 32 hex digits
864 * which are the ascii representations of a 16 byte string.
865 * Get two at a time and put them into the password.
867 p++;
869 /* Record exact password position */
870 pwd_seekpos += PTR_DIFF(p, linebuf);
872 if (!override && (*p == '*' || *p == 'X')) {
873 /* Password deliberately invalid - end here. */
874 DEBUG(10, ("mod_smbfilepwd_entry: entry invalidated for user %s\n", user_name));
875 pw_file_unlock(lockfd, &pw_file_lock_depth);
876 fclose(fp);
877 return False;
880 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
881 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
882 pw_file_unlock(lockfd,&pw_file_lock_depth);
883 fclose(fp);
884 return (False);
887 if (p[32] != ':') {
888 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
889 pw_file_unlock(lockfd,&pw_file_lock_depth);
890 fclose(fp);
891 return False;
894 if (!override && (*p == '*' || *p == 'X')) {
895 pw_file_unlock(lockfd,&pw_file_lock_depth);
896 fclose(fp);
897 return False;
900 /* Now check if the NT compatible password is
901 available. */
902 p += 33; /* Move to the first character of the line after
903 the lanman password. */
904 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
905 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
906 pw_file_unlock(lockfd,&pw_file_lock_depth);
907 fclose(fp);
908 return (False);
911 if (p[32] != ':') {
912 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
913 pw_file_unlock(lockfd,&pw_file_lock_depth);
914 fclose(fp);
915 return False;
919 * Now check if the account info and the password last
920 * change time is available.
922 p += 33; /* Move to the first character of the line after
923 the NT password. */
926 * If both NT and lanman passwords are provided - reset password
927 * not required flag.
930 if(pwd->smb_passwd != NULL || pwd->smb_nt_passwd != NULL) {
931 /* Reqiure password in the future (should ACB_DISABLED also be reset?) */
932 pwd->acct_ctrl &= ~(ACB_PWNOTREQ);
935 if (*p == '[') {
937 i = 0;
938 encode_bits[i++] = *p++;
939 while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']'))
940 encode_bits[i++] = *p++;
942 encode_bits[i++] = ']';
943 encode_bits[i++] = '\0';
945 if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
947 * We are using a new format, space padded
948 * acct ctrl field. Encode the given acct ctrl
949 * bits into it.
951 fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
952 } else {
954 * If using the old format and the ACB_DISABLED or
955 * ACB_PWNOTREQ are set then set the lanman and NT passwords to NULL
956 * here as we have no space to encode the change.
958 if(pwd->acct_ctrl & (ACB_DISABLED|ACB_PWNOTREQ)) {
959 pwd->smb_passwd = NULL;
960 pwd->smb_nt_passwd = NULL;
964 /* Go past the ']' */
965 if(linebuf_len > PTR_DIFF(p, linebuf))
966 p++;
968 if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
969 p++;
971 /* We should be pointing at the LCT entry. */
972 if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
974 p += 4;
975 for(i = 0; i < 8; i++) {
976 if(p[i] == '\0' || !isxdigit(p[i]))
977 break;
979 if(i == 8) {
981 * p points at 8 characters of hex digits -
982 * read into a time_t as the seconds since
983 * 1970 that the password was last changed.
985 got_pass_last_set_time = True;
986 } /* i == 8 */
987 } /* *p && StrnCaseCmp() */
988 } /* p == ':' */
989 } /* p == '[' */
991 /* Entry is correctly formed. */
993 /* Create the 32 byte representation of the new p16 */
994 if(pwd->smb_passwd != NULL) {
995 for (i = 0; i < 16; i++) {
996 slprintf(&ascii_p16[i*2], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_passwd[i]);
998 } else {
999 if(pwd->acct_ctrl & ACB_PWNOTREQ)
1000 fstrcpy(ascii_p16, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
1001 else
1002 fstrcpy(ascii_p16, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
1005 /* Add on the NT md4 hash */
1006 ascii_p16[32] = ':';
1007 wr_len = 66;
1008 if (pwd->smb_nt_passwd != NULL) {
1009 for (i = 0; i < 16; i++) {
1010 slprintf(&ascii_p16[(i*2)+33], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_nt_passwd[i]);
1012 } else {
1013 if(pwd->acct_ctrl & ACB_PWNOTREQ)
1014 fstrcpy(&ascii_p16[33], "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
1015 else
1016 fstrcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
1018 ascii_p16[65] = ':';
1019 ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
1021 /* Add on the account info bits and the time of last
1022 password change. */
1024 pwd->pass_last_set_time = time(NULL);
1026 if(got_pass_last_set_time) {
1027 slprintf(&ascii_p16[strlen(ascii_p16)],
1028 sizeof(ascii_p16)-(strlen(ascii_p16)+1),
1029 "%s:LCT-%08X:",
1030 encode_bits, (uint32)pwd->pass_last_set_time );
1031 wr_len = strlen(ascii_p16);
1034 #ifdef DEBUG_PASSWORD
1035 DEBUG(100,("mod_smbfilepwd_entry: "));
1036 dump_data(100, ascii_p16, wr_len);
1037 #endif
1039 if(wr_len > sizeof(linebuf)) {
1040 DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
1041 pw_file_unlock(lockfd,&pw_file_lock_depth);
1042 fclose(fp);
1043 return (False);
1047 * Do an atomic write into the file at the position defined by
1048 * seekpos.
1051 /* The mod user write needs to be atomic - so get the fd from
1052 the fp and do a raw write() call.
1055 fd = fileno(fp);
1057 if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
1058 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1059 pw_file_unlock(lockfd,&pw_file_lock_depth);
1060 fclose(fp);
1061 return False;
1064 /* Sanity check - ensure the areas we are writing are framed by ':' */
1065 if (read(fd, linebuf, wr_len+1) != wr_len+1) {
1066 DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
1067 pw_file_unlock(lockfd,&pw_file_lock_depth);
1068 fclose(fp);
1069 return False;
1072 if ((linebuf[0] != ':') || (linebuf[wr_len] != ':')) {
1073 DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
1074 pw_file_unlock(lockfd,&pw_file_lock_depth);
1075 fclose(fp);
1076 return False;
1079 if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
1080 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1081 pw_file_unlock(lockfd,&pw_file_lock_depth);
1082 fclose(fp);
1083 return False;
1086 if (write(fd, ascii_p16, wr_len) != wr_len) {
1087 DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
1088 pw_file_unlock(lockfd,&pw_file_lock_depth);
1089 fclose(fp);
1090 return False;
1093 pw_file_unlock(lockfd,&pw_file_lock_depth);
1094 fclose(fp);
1095 return True;
1098 /************************************************************************
1099 Routine to delete an entry in the smbpasswd file by name.
1100 *************************************************************************/
1102 static BOOL del_smbfilepwd_entry(const char *name)
1104 char *pfile = lp_smb_passwd_file();
1105 pstring pfile2;
1106 struct smb_passwd *pwd = NULL;
1107 FILE *fp = NULL;
1108 FILE *fp_write = NULL;
1109 int pfile2_lockdepth = 0;
1111 slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)sys_getpid() );
1114 * Open the smbpassword file - for update. It needs to be update
1115 * as we need any other processes to wait until we have replaced
1116 * it.
1119 if((fp = startsmbfilepwent(True)) == NULL) {
1120 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1121 return False;
1125 * Create the replacement password file.
1127 if((fp_write = startsmbfilepwent_internal(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
1128 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1129 endsmbfilepwent(fp);
1130 return False;
1134 * Scan the file, a line at a time and check if the name matches.
1137 while ((pwd = getsmbfilepwent(fp)) != NULL) {
1138 char *new_entry;
1139 size_t new_entry_length;
1141 if (strequal(name, pwd->smb_name)) {
1142 DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name));
1143 continue;
1147 * We need to copy the entry out into the second file.
1150 if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) {
1151 DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
1152 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1153 unlink(pfile2);
1154 endsmbfilepwent(fp);
1155 endsmbfilepwent_internal(fp_write,&pfile2_lockdepth);
1156 return False;
1159 new_entry_length = strlen(new_entry);
1161 if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) {
1162 DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
1163 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1164 unlink(pfile2);
1165 endsmbfilepwent(fp);
1166 endsmbfilepwent_internal(fp_write,&pfile2_lockdepth);
1167 free(new_entry);
1168 return False;
1171 free(new_entry);
1175 * Ensure pfile2 is flushed before rename.
1178 if(fflush(fp_write) != 0) {
1179 DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
1180 endsmbfilepwent(fp);
1181 endsmbfilepwent_internal(fp_write,&pfile2_lockdepth);
1182 return False;
1186 * Do an atomic rename - then release the locks.
1189 if(rename(pfile2,pfile) != 0) {
1190 unlink(pfile2);
1192 endsmbfilepwent(fp);
1193 endsmbfilepwent_internal(fp_write,&pfile2_lockdepth);
1194 return True;
1198 * Stub functions - implemented in terms of others.
1201 static BOOL mod_smbfile21pwd_entry(struct sam_passwd* pwd, BOOL override)
1203 return mod_smbfilepwd_entry(pdb_sam_to_smb(pwd), override);
1206 static BOOL add_smbfile21pwd_entry(struct sam_passwd *newpwd)
1208 return add_smbfilepwd_entry(pdb_sam_to_smb(newpwd));
1211 static struct sam_disp_info *getsmbfiledispnam(char *name)
1213 return pdb_sam_to_dispinfo(getsam21pwnam(name));
1216 static struct sam_disp_info *getsmbfiledisprid(uint32 rid)
1218 return pdb_sam_to_dispinfo(getsam21pwrid(rid));
1221 static struct sam_disp_info *getsmbfiledispent(void *vp)
1223 return pdb_sam_to_dispinfo(getsam21pwent(vp));
1226 static struct passdb_ops file_ops = {
1227 startsmbfilepwent,
1228 endsmbfilepwent,
1229 getsmbfilepwpos,
1230 setsmbfilepwpos,
1231 iterate_getsmbpwnam, /* In passdb.c */
1232 iterate_getsmbpwuid, /* In passdb.c */
1233 iterate_getsmbpwrid, /* In passdb.c */
1234 getsmbfilepwent,
1235 add_smbfilepwd_entry,
1236 mod_smbfilepwd_entry,
1237 del_smbfilepwd_entry,
1238 getsmbfile21pwent,
1239 iterate_getsam21pwnam,
1240 iterate_getsam21pwuid,
1241 iterate_getsam21pwrid,
1242 add_smbfile21pwd_entry,
1243 mod_smbfile21pwd_entry,
1244 getsmbfiledispnam,
1245 getsmbfiledisprid,
1246 getsmbfiledispent
1249 struct passdb_ops *file_initialize_password_db(void)
1251 return &file_ops;
1254 #else
1255 /* Do *NOT* make this function static. It breaks the compile on gcc. JRA */
1256 void smbpass_dummy_function(void) { } /* stop some compilers complaining */
1257 #endif /* USE_SMBPASS_DB */