Correct PPTP server firewall rules chain.
[tomato/davidwu.git] / release / src / router / samba / source / passdb / smbpass.c
blob7aad048339770e22930daa65dcc1ab1f3f4568df
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;
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 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;
247 } else
248 linebuf[linebuf_len - 1] = '\0';
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;
258 * The line we have should be of the form :-
260 * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
261 * ignored....
263 * or,
265 * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
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.
272 if (linebuf[0] == '#' || linebuf[0] == '\0') {
273 DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
274 continue;
276 p = (unsigned char *) strchr(linebuf, ':');
277 if (p == NULL) {
278 DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
279 continue;
282 * As 256 is shorter than a pstring we don't need to check
283 * length here - if this ever changes....
285 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
286 user_name[PTR_DIFF(p, linebuf)] = '\0';
288 /* Get smb uid. */
290 p++; /* Go past ':' */
292 if(*p == '-') {
293 DEBUG(0, ("getsmbfilepwent: uids in the smbpasswd file must not be negative.\n"));
294 continue;
297 if (!isdigit(*p)) {
298 DEBUG(0, ("getsmbfilepwent: malformed password entry (uid not number)\n"));
299 continue;
302 uidval = atoi((char *) p);
304 while (*p && isdigit(*p))
305 p++;
307 if (*p != ':') {
308 DEBUG(0, ("getsmbfilepwent: malformed password entry (no : after uid)\n"));
309 continue;
312 pw_buf.smb_name = user_name;
313 pw_buf.smb_userid = uidval;
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.
321 /* Skip the ':' */
322 p++;
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;
333 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
334 DEBUG(0, ("getsmbfilepwent: malformed password entry (passwd too short)\n"));
335 continue;
338 if (p[32] != ':') {
339 DEBUG(0, ("getsmbfilepwent: malformed password entry (no terminating :)\n"));
340 continue;
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;
351 pw_buf.smb_passwd = smbpwd;
355 * Now check if the NT compatible password is
356 * available.
358 pw_buf.smb_nt_passwd = NULL;
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;
367 p += 33; /* Move to the first character of the line after
368 the NT password. */
371 DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
372 user_name, uidval));
374 if (*p == '[')
376 unsigned char *end_p = (unsigned char *)strchr((char *)p, ']');
377 pw_buf.acct_ctrl = pdb_decode_acct_ctrl((char*)p);
379 /* Must have some account type set. */
380 if(pw_buf.acct_ctrl == 0)
381 pw_buf.acct_ctrl = ACB_NORMAL;
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;
395 if(i == 8) {
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.
401 pw_buf.pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
405 } else {
406 /* 'Old' style file. Fake up based on user name. */
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.
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;
418 return &pw_buf;
421 DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
422 return NULL;
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 *************************************************************************/
433 static struct sam_passwd *getsmbfile21pwent(void *vp)
435 struct smb_passwd *pw_buf = getsmbfilepwent(vp);
436 static struct sam_passwd user;
437 struct passwd *pwfile;
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;
447 DEBUG(5,("getsmbfile21pwent\n"));
449 if (pw_buf == NULL) return NULL;
451 pwfile = sys_getpwnam(pw_buf->smb_name);
452 if (pwfile == NULL)
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;
459 pdb_init_sam(&user);
461 pstrcpy(samlogon_user, pw_buf->smb_name);
463 if (samlogon_user[strlen(samlogon_user)-1] != '$')
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;
469 user.smb_userid = pw_buf->smb_userid;
470 user.smb_grpid = pwfile->pw_gid;
472 user.user_rid = pdb_uid_to_user_rid (user.smb_userid);
473 user.group_rid = pdb_gid_to_group_rid(user.smb_grpid );
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 , "");
483 sam_logon_in_ssb = False;
485 else
487 user.smb_userid = pw_buf->smb_userid;
488 user.smb_grpid = pwfile->pw_gid;
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. */
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 , "");
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;
511 user.unknown_str = NULL; /* don't know, yet! */
512 user.munged_dial = NULL; /* "munged" dial-back telephone number */
514 user.smb_nt_passwd = pw_buf->smb_nt_passwd;
515 user.smb_passwd = pw_buf->smb_passwd;
517 user.acct_ctrl = pw_buf->acct_ctrl;
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 */
526 return &user;
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 *************************************************************************/
534 static SMB_BIG_UINT getsmbfilepwpos(void *vp)
536 return (SMB_BIG_UINT)sys_ftell((FILE *)vp);
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 *************************************************************************/
544 static BOOL setsmbfilepwpos(void *vp, SMB_BIG_UINT tok)
546 return !sys_fseek((FILE *)vp, (SMB_OFF_T)tok, SEEK_SET);
549 /************************************************************************
550 Create a new smbpasswd entry - malloced space returned.
551 *************************************************************************/
553 char *format_new_smbpasswd_entry(struct smb_passwd *newpwd)
555 int new_entry_length;
556 char *new_entry;
557 char *p;
558 int i;
560 new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
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;
567 slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
568 p = &new_entry[strlen(new_entry)];
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]);
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));
582 p += 32;
584 *p++ = ':';
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]);
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));
597 p += 32;
599 *p++ = ':';
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);
606 return new_entry;
609 /************************************************************************
610 Routine to add an entry to the smbpasswd file.
611 *************************************************************************/
613 static BOOL add_smbfilepwd_entry(struct smb_passwd *newpwd)
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;
624 /* Open the smbpassword file - for update. */
625 fp = startsmbfilepwent(True);
627 if (fp == NULL) {
628 DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
629 return False;
633 * Scan the file, a line at a time and check if the name matches.
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;
644 /* Ok - entry doesn't exist. We can add it */
646 /* Create a new smb passwd entry and set it to the given password. */
648 * The add user write needs to be atomic - so get the fd from
649 * the fp and do a raw write() call.
651 fd = fileno(fp);
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. \
655 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
656 endsmbfilepwent(fp);
657 return False;
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. \
662 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
663 endsmbfilepwent(fp);
664 return False;
667 new_entry_length = strlen(new_entry);
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
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. \
676 Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
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. \
681 Error was %s. Password file may be corrupt ! Please examine by hand !\n",
682 newpwd->smb_name, strerror(errno)));
685 endsmbfilepwent(fp);
686 free(new_entry);
687 return False;
690 free(new_entry);
691 endsmbfilepwent(fp);
692 return True;
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 ************************************************************************/
704 static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
706 /* Static buffers we will return. */
707 static pstring user_name;
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;
722 SMB_OFF_T pwd_seekpos = 0;
724 int i;
725 int wr_len;
726 int fd;
728 if (!*pfile) {
729 DEBUG(0, ("No SMB password file set\n"));
730 return False;
732 DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
734 fp = sys_fopen(pfile, "r+");
736 if (fp == NULL) {
737 DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
738 return False;
740 /* Set a buffer to do more efficient reads */
741 setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
743 lockfd = fileno(fp);
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;
751 /* Make sure it is only rw by the owner */
752 chmod(pfile, 0600);
754 /* We have a write lock on the file. */
756 * Scan the file, a line at a time and check if the name matches.
758 while (!feof(fp)) {
759 pwd_seekpos = sys_ftell(fp);
761 linebuf[0] = '\0';
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;
771 * Check if the string is terminated with a newline - if not
772 * then we must keep reading and discard until we get one.
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;
783 } else {
784 linebuf[linebuf_len - 1] = '\0';
787 #ifdef DEBUG_PASSWORD
788 DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
789 #endif
791 if ((linebuf[0] == 0) && feof(fp)) {
792 DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
793 break;
797 * The line we have should be of the form :-
799 * username:uid:[32hex bytes]:....other flags presently
800 * ignored....
802 * or,
804 * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
806 * if Windows NT compatible passwords are also present.
809 if (linebuf[0] == '#' || linebuf[0] == '\0') {
810 DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
811 continue;
814 p = (unsigned char *) strchr(linebuf, ':');
816 if (p == NULL) {
817 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
818 continue;
822 * As 256 is shorter than a pstring we don't need to check
823 * length here - if this ever changes....
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;
833 if (!found_entry) {
834 pw_file_unlock(lockfd, &pw_file_lock_depth);
835 fclose(fp);
836 return False;
839 DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n"));
841 /* User name matches - get uid and password */
842 p++; /* Go past ':' */
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;
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;
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.
865 p++;
867 /* Record exact password position */
868 pwd_seekpos += PTR_DIFF(p, linebuf);
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;
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);
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;
892 if (!override && (*p == '*' || *p == 'X')) {
893 pw_file_unlock(lockfd,&pw_file_lock_depth);
894 fclose(fp);
895 return False;
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);
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;
917 * Now check if the account info and the password last
918 * change time is available.
920 p += 33; /* Move to the first character of the line after
921 the NT password. */
924 * If both NT and lanman passwords are provided - reset password
925 * not required flag.
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);
933 if (*p == '[') {
935 i = 0;
936 encode_bits[i++] = *p++;
937 while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']'))
938 encode_bits[i++] = *p++;
940 encode_bits[i++] = ']';
941 encode_bits[i++] = '\0';
943 if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
945 * We are using a new format, space padded
946 * acct ctrl field. Encode the given acct ctrl
947 * bits into it.
949 fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
950 } else {
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.
956 if(pwd->acct_ctrl & (ACB_DISABLED|ACB_PWNOTREQ)) {
957 pwd->smb_passwd = NULL;
958 pwd->smb_nt_passwd = NULL;
962 /* Go past the ']' */
963 if(linebuf_len > PTR_DIFF(p, linebuf))
964 p++;
966 if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
967 p++;
969 /* We should be pointing at the LCT entry. */
970 if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
972 p += 4;
973 for(i = 0; i < 8; i++) {
974 if(p[i] == '\0' || !isxdigit(p[i]))
975 break;
977 if(i == 8) {
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.
983 got_pass_last_set_time = True;
984 } /* i == 8 */
985 } /* *p && StrnCaseCmp() */
986 } /* p == ':' */
987 } /* p == '[' */
989 /* Entry is correctly formed. */
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]);
996 } else {
997 if(pwd->acct_ctrl & ACB_PWNOTREQ)
998 fstrcpy(ascii_p16, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
999 else
1000 fstrcpy(ascii_p16, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
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]);
1010 } else {
1011 if(pwd->acct_ctrl & ACB_PWNOTREQ)
1012 fstrcpy(&ascii_p16[33], "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
1013 else
1014 fstrcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
1016 ascii_p16[65] = ':';
1017 ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
1019 /* Add on the account info bits and the time of last
1020 password change. */
1022 pwd->pass_last_set_time = time(NULL);
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);
1032 #ifdef DEBUG_PASSWORD
1033 DEBUG(100,("mod_smbfilepwd_entry: "));
1034 dump_data(100, ascii_p16, wr_len);
1035 #endif
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);
1045 * Do an atomic write into the file at the position defined by
1046 * seekpos.
1049 /* The mod user write needs to be atomic - so get the fd from
1050 the fp and do a raw write() call.
1053 fd = fileno(fp);
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;
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;
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;
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;
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;
1091 pw_file_unlock(lockfd,&pw_file_lock_depth);
1092 fclose(fp);
1093 return True;
1096 /************************************************************************
1097 Routine to delete an entry in the smbpasswd file by name.
1098 *************************************************************************/
1100 static BOOL del_smbfilepwd_entry(const char *name)
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;
1109 slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)getpid() );
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.
1117 if((fp = startsmbfilepwent(True)) == NULL) {
1118 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1119 return False;
1123 * Create the replacement password file.
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;
1132 * Scan the file, a line at a time and check if the name matches.
1135 while ((pwd = getsmbfilepwent(fp)) != NULL) {
1136 char *new_entry;
1137 size_t new_entry_length;
1139 if (strequal(name, pwd->smb_name)) {
1140 DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name));
1141 continue;
1145 * We need to copy the entry out into the second file.
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. \
1150 Error 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;
1157 new_entry_length = strlen(new_entry);
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. \
1161 Error 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;
1169 free(new_entry);
1173 * Ensure pfile2 is flushed before rename.
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;
1184 * Do an atomic rename - then release the locks.
1187 if(rename(pfile2,pfile) != 0) {
1188 unlink(pfile2);
1190 endsmbfilepwent(fp);
1191 endsmbfilepwent_internal(fp_write,&pfile2_lockdepth);
1192 return True;
1196 * Stub functions - implemented in terms of others.
1199 static BOOL mod_smbfile21pwd_entry(struct sam_passwd* pwd, BOOL override)
1201 return mod_smbfilepwd_entry(pdb_sam_to_smb(pwd), override);
1204 static BOOL add_smbfile21pwd_entry(struct sam_passwd *newpwd)
1206 return add_smbfilepwd_entry(pdb_sam_to_smb(newpwd));
1209 static struct sam_disp_info *getsmbfiledispnam(char *name)
1211 return pdb_sam_to_dispinfo(getsam21pwnam(name));
1214 static struct sam_disp_info *getsmbfiledisprid(uint32 rid)
1216 return pdb_sam_to_dispinfo(getsam21pwrid(rid));
1219 static struct sam_disp_info *getsmbfiledispent(void *vp)
1221 return pdb_sam_to_dispinfo(getsam21pwent(vp));
1224 static 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
1247 struct passdb_ops *file_initialize_password_db(void)
1249 return &file_ops;
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 */