r23779: Change from v2 or later to v3 or later.
[sfrench/samba-autobuild/.git] / source / passdb / pdb_smbpasswd.c
1 /*
2  * Unix SMB/CIFS implementation. 
3  * SMB parameters and setup
4  * Copyright (C) Andrew Tridgell       1992-1998 
5  * Modified by Jeremy Allison          1995.
6  * Modified by Gerald (Jerry) Carter   2000-2001,2003
7  * Modified by Andrew Bartlett         2002.
8  * 
9  * This program is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free
11  * Software Foundation; either version 3 of the License, or (at your option)
12  * any later version.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17  * more details.
18  * 
19  * You should have received a copy of the GNU General Public License along with
20  * this program; if not, write to the Free Software Foundation, Inc., 675
21  * Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 #include "includes.h"
25
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_PASSDB
28
29 /* 
30    smb_passwd is analogous to sam_passwd used everywhere
31    else.  However, smb_passwd is limited to the information
32    stored by an smbpasswd entry 
33  */
34  
35 struct smb_passwd
36 {
37         uint32 smb_userid;        /* this is actually the unix uid_t */
38         const char *smb_name;     /* username string */
39
40         const unsigned char *smb_passwd;    /* Null if no password */
41         const unsigned char *smb_nt_passwd; /* Null if no password */
42
43         uint16 acct_ctrl;             /* account info (ACB_xxxx bit-mask) */
44         time_t pass_last_set_time;    /* password last set time */
45 };
46
47 struct smbpasswd_privates
48 {
49         /* used for maintain locks on the smbpasswd file */
50         int     pw_file_lock_depth;
51         
52         /* Global File pointer */
53         FILE    *pw_file;
54         
55         /* formerly static variables */
56         struct smb_passwd pw_buf;
57         pstring  user_name;
58         unsigned char smbpwd[16];
59         unsigned char smbntpwd[16];
60
61         /* retrive-once info */
62         const char *smbpasswd_file;
63 };
64
65 enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE };
66
67 static SIG_ATOMIC_T gotalarm;
68
69 /***************************************************************
70  Signal function to tell us we timed out.
71 ****************************************************************/
72
73 static void gotalarm_sig(void)
74 {
75         gotalarm = 1;
76 }
77
78 /***************************************************************
79  Lock or unlock a fd for a known lock type. Abandon after waitsecs 
80  seconds.
81 ****************************************************************/
82
83 static BOOL do_file_lock(int fd, int waitsecs, int type)
84 {
85         SMB_STRUCT_FLOCK lock;
86         int             ret;
87         void (*oldsig_handler)(int);
88
89         gotalarm = 0;
90         oldsig_handler = CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
91
92         lock.l_type = type;
93         lock.l_whence = SEEK_SET;
94         lock.l_start = 0;
95         lock.l_len = 1;
96         lock.l_pid = 0;
97
98         alarm(waitsecs);
99         /* Note we must *NOT* use sys_fcntl here ! JRA */
100         ret = fcntl(fd, SMB_F_SETLKW, &lock);
101         alarm(0);
102         CatchSignal(SIGALRM, SIGNAL_CAST oldsig_handler);
103
104         if (gotalarm) {
105                 DEBUG(0, ("do_file_lock: failed to %s file.\n",
106                         type == F_UNLCK ? "unlock" : "lock"));
107                 return False;
108         }
109
110         return (ret == 0);
111 }
112
113 /***************************************************************
114  Lock an fd. Abandon after waitsecs seconds.
115 ****************************************************************/
116
117 static BOOL pw_file_lock(int fd, int type, int secs, int *plock_depth)
118 {
119         if (fd < 0) {
120                 return False;
121         }
122
123         if(*plock_depth == 0) {
124                 if (!do_file_lock(fd, secs, type)) {
125                         DEBUG(10,("pw_file_lock: locking file failed, error = %s.\n",
126                                 strerror(errno)));
127                         return False;
128                 }
129         }
130
131         (*plock_depth)++;
132
133         return True;
134 }
135
136 /***************************************************************
137  Unlock an fd. Abandon after waitsecs seconds.
138 ****************************************************************/
139
140 static BOOL pw_file_unlock(int fd, int *plock_depth)
141 {
142         BOOL ret=True;
143
144         if (fd == 0 || *plock_depth == 0) {
145                 return True;
146         }
147
148         if(*plock_depth == 1) {
149                 ret = do_file_lock(fd, 5, F_UNLCK);
150         }
151
152         if (*plock_depth > 0) {
153                 (*plock_depth)--;
154         }
155
156         if(!ret) {
157                 DEBUG(10,("pw_file_unlock: unlocking file failed, error = %s.\n",
158                         strerror(errno)));
159         }
160         return ret;
161 }
162
163 /**************************************************************
164  Intialize a smb_passwd struct
165  *************************************************************/
166
167 static void pdb_init_smb(struct smb_passwd *user)
168 {
169         if (user == NULL) 
170                 return;
171         ZERO_STRUCTP (user);
172         
173         user->pass_last_set_time = (time_t)0;
174 }
175
176 /***************************************************************
177  Internal fn to enumerate the smbpasswd list. Returns a void pointer
178  to ensure no modification outside this module. Checks for atomic
179  rename of smbpasswd file on update or create once the lock has
180  been granted to prevent race conditions. JRA.
181 ****************************************************************/
182
183 static FILE *startsmbfilepwent(const char *pfile, enum pwf_access_type type, int *lock_depth)
184 {
185         FILE *fp = NULL;
186         const char *open_mode = NULL;
187         int race_loop = 0;
188         int lock_type = F_RDLCK;
189
190         if (!*pfile) {
191                 DEBUG(0, ("startsmbfilepwent: No SMB password file set\n"));
192                 return (NULL);
193         }
194
195         switch(type) {
196                 case PWF_READ:
197                         open_mode = "rb";
198                         lock_type = F_RDLCK;
199                         break;
200                 case PWF_UPDATE:
201                         open_mode = "r+b";
202                         lock_type = F_WRLCK;
203                         break;
204                 case PWF_CREATE:
205                         /*
206                          * Ensure atomic file creation.
207                          */
208                         {
209                                 int i, fd = -1;
210
211                                 for(i = 0; i < 5; i++) {
212                                         if((fd = sys_open(pfile, O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600))!=-1) {
213                                                 break;
214                                         }
215                                         sys_usleep(200); /* Spin, spin... */
216                                 }
217                                 if(fd == -1) {
218                                         DEBUG(0,("startsmbfilepwent_internal: too many race conditions \
219 creating file %s\n", pfile));
220                                         return NULL;
221                                 }
222                                 close(fd);
223                                 open_mode = "r+b";
224                                 lock_type = F_WRLCK;
225                                 break;
226                         }
227         }
228                        
229         for(race_loop = 0; race_loop < 5; race_loop++) {
230                 DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile));
231
232                 if((fp = sys_fopen(pfile, open_mode)) == NULL) {
233
234                         /*
235                          * If smbpasswd file doesn't exist, then create new one. This helps to avoid
236                          * confusing error msg when adding user account first time.
237                          */
238                         if (errno == ENOENT) {
239                                 if ((fp = sys_fopen(pfile, "a+")) != NULL) {
240                                         DEBUG(0, ("startsmbfilepwent_internal: file %s did not \
241 exist. File successfully created.\n", pfile));
242                                 } else {
243                                         DEBUG(0, ("startsmbfilepwent_internal: file %s did not \
244 exist. Couldn't create new one. Error was: %s",
245                                         pfile, strerror(errno)));
246                                         return NULL;
247                                 }
248                         } else {
249                                 DEBUG(0, ("startsmbfilepwent_internal: unable to open file %s. \
250 Error was: %s\n", pfile, strerror(errno)));
251                                 return NULL;
252                         }
253                 }
254
255                 if (!pw_file_lock(fileno(fp), lock_type, 5, lock_depth)) {
256                         DEBUG(0, ("startsmbfilepwent_internal: unable to lock file %s. \
257 Error was %s\n", pfile, strerror(errno) ));
258                         fclose(fp);
259                         return NULL;
260                 }
261
262                 /*
263                  * Only check for replacement races on update or create.
264                  * For read we don't mind if the data is one record out of date.
265                  */
266
267                 if(type == PWF_READ) {
268                         break;
269                 } else {
270                         SMB_STRUCT_STAT sbuf1, sbuf2;
271
272                         /*
273                          * Avoid the potential race condition between the open and the lock
274                          * by doing a stat on the filename and an fstat on the fd. If the
275                          * two inodes differ then someone did a rename between the open and
276                          * the lock. Back off and try the open again. Only do this 5 times to
277                          * prevent infinate loops. JRA.
278                          */
279
280                         if (sys_stat(pfile,&sbuf1) != 0) {
281                                 DEBUG(0, ("startsmbfilepwent_internal: unable to stat file %s. \
282 Error was %s\n", pfile, strerror(errno)));
283                                 pw_file_unlock(fileno(fp), lock_depth);
284                                 fclose(fp);
285                                 return NULL;
286                         }
287
288                         if (sys_fstat(fileno(fp),&sbuf2) != 0) {
289                                 DEBUG(0, ("startsmbfilepwent_internal: unable to fstat file %s. \
290 Error was %s\n", pfile, strerror(errno)));
291                                 pw_file_unlock(fileno(fp), lock_depth);
292                                 fclose(fp);
293                                 return NULL;
294                         }
295
296                         if( sbuf1.st_ino == sbuf2.st_ino) {
297                                 /* No race. */
298                                 break;
299                         }
300
301                         /*
302                          * Race occurred - back off and try again...
303                          */
304
305                         pw_file_unlock(fileno(fp), lock_depth);
306                         fclose(fp);
307                 }
308         }
309
310         if(race_loop == 5) {
311                 DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile));
312                 return NULL;
313         }
314
315         /* Set a buffer to do more efficient reads */
316         setvbuf(fp, (char *)NULL, _IOFBF, 1024);
317
318         /* Make sure it is only rw by the owner */
319 #ifdef HAVE_FCHMOD
320         if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) {
321 #else
322         if(chmod(pfile, S_IRUSR|S_IWUSR) == -1) {
323 #endif
324                 DEBUG(0, ("startsmbfilepwent_internal: failed to set 0600 permissions on password file %s. \
325 Error was %s\n.", pfile, strerror(errno) ));
326                 pw_file_unlock(fileno(fp), lock_depth);
327                 fclose(fp);
328                 return NULL;
329         }
330
331         /* We have a lock on the file. */
332         return fp;
333 }
334
335 /***************************************************************
336  End enumeration of the smbpasswd list.
337 ****************************************************************/
338
339 static void endsmbfilepwent(FILE *fp, int *lock_depth)
340 {
341         if (!fp) {
342                 return;
343         }
344
345         pw_file_unlock(fileno(fp), lock_depth);
346         fclose(fp);
347         DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n"));
348 }
349
350 /*************************************************************************
351  Routine to return the next entry in the smbpasswd list.
352  *************************************************************************/
353
354 static struct smb_passwd *getsmbfilepwent(struct smbpasswd_privates *smbpasswd_state, FILE *fp)
355 {
356         /* Static buffers we will return. */
357         struct smb_passwd *pw_buf = &smbpasswd_state->pw_buf;
358         char  *user_name = smbpasswd_state->user_name;
359         unsigned char *smbpwd = smbpasswd_state->smbpwd;
360         unsigned char *smbntpwd = smbpasswd_state->smbntpwd;
361         char linebuf[256];
362         int c;
363         unsigned char *p;
364         long uidval;
365         size_t linebuf_len;
366         char *status;
367
368         if(fp == NULL) {
369                 DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
370                 return NULL;
371         }
372
373         pdb_init_smb(pw_buf);
374         pw_buf->acct_ctrl = ACB_NORMAL;  
375
376         /*
377          * Scan the file, a line at a time and check if the name matches.
378          */
379         status = linebuf;
380         while (status && !feof(fp)) {
381                 linebuf[0] = '\0';
382
383                 status = fgets(linebuf, 256, fp);
384                 if (status == NULL && ferror(fp)) {
385                         return NULL;
386                 }
387
388                 /*
389                  * Check if the string is terminated with a newline - if not
390                  * then we must keep reading and discard until we get one.
391                  */
392                 if ((linebuf_len = strlen(linebuf)) == 0) {
393                         continue;
394                 }
395
396                 if (linebuf[linebuf_len - 1] != '\n') {
397                         c = '\0';
398                         while (!ferror(fp) && !feof(fp)) {
399                                 c = fgetc(fp);
400                                 if (c == '\n') {
401                                         break;
402                                 }
403                         }
404                 } else {
405                         linebuf[linebuf_len - 1] = '\0';
406                 }
407
408 #ifdef DEBUG_PASSWORD
409                 DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
410 #endif
411                 if ((linebuf[0] == 0) && feof(fp)) {
412                         DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
413                         break;
414                 }
415
416                 /*
417                  * The line we have should be of the form :-
418                  * 
419                  * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
420                  * ignored....
421                  * 
422                  * or,
423                  *
424                  * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
425                  *
426                  * if Windows NT compatible passwords are also present.
427                  * [Account type] is an ascii encoding of the type of account.
428                  * LCT-(8 hex digits) is the time_t value of the last change time.
429                  */
430
431                 if (linebuf[0] == '#' || linebuf[0] == '\0') {
432                         DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
433                         continue;
434                 }
435                 p = (unsigned char *) strchr_m(linebuf, ':');
436                 if (p == NULL) {
437                         DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
438                         continue;
439                 }
440
441                 /*
442                  * As 256 is shorter than a pstring we don't need to check
443                  * length here - if this ever changes....
444                  */
445                 SMB_ASSERT(sizeof(pstring) > sizeof(linebuf));
446
447                 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
448                 user_name[PTR_DIFF(p, linebuf)] = '\0';
449
450                 /* Get smb uid. */
451
452                 p++; /* Go past ':' */
453
454                 if(*p == '-') {
455                         DEBUG(0, ("getsmbfilepwent: user name %s has a negative uid.\n", user_name));
456                         continue;
457                 }
458
459                 if (!isdigit(*p)) {
460                         DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (uid not number)\n",
461                                 user_name));
462                         continue;
463                 }
464
465                 uidval = atoi((char *) p);
466
467                 while (*p && isdigit(*p)) {
468                         p++;
469                 }
470
471                 if (*p != ':') {
472                         DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (no : after uid)\n",
473                                 user_name));
474                         continue;
475                 }
476
477                 pw_buf->smb_name = user_name;
478                 pw_buf->smb_userid = uidval;
479
480                 /*
481                  * Now get the password value - this should be 32 hex digits
482                  * which are the ascii representations of a 16 byte string.
483                  * Get two at a time and put them into the password.
484                  */
485
486                 /* Skip the ':' */
487                 p++;
488
489                 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
490                         DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (passwd too short)\n",
491                                 user_name ));
492                         continue;
493                 }
494
495                 if (p[32] != ':') {
496                         DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (no terminating :)\n",
497                                 user_name));
498                         continue;
499                 }
500
501                 if (strnequal((char *) p, "NO PASSWORD", 11)) {
502                         pw_buf->smb_passwd = NULL;
503                         pw_buf->acct_ctrl |= ACB_PWNOTREQ;
504                 } else {
505                         if (*p == '*' || *p == 'X') {
506                                 /* NULL LM password */
507                                 pw_buf->smb_passwd = NULL;
508                                 DEBUG(10, ("getsmbfilepwent: LM password for user %s invalidated\n", user_name));
509                         } else if (pdb_gethexpwd((char *)p, smbpwd)) {
510                                 pw_buf->smb_passwd = smbpwd;
511                         } else {
512                                 pw_buf->smb_passwd = NULL;
513                                 DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry for user %s \
514 (non hex chars)\n", user_name));
515                         }
516                 }
517
518                 /* 
519                  * Now check if the NT compatible password is
520                  * available.
521                  */
522                 pw_buf->smb_nt_passwd = NULL;
523                 p += 33; /* Move to the first character of the line after the lanman password. */
524                 if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
525                         if (*p != '*' && *p != 'X') {
526                                 if(pdb_gethexpwd((char *)p,smbntpwd)) {
527                                         pw_buf->smb_nt_passwd = smbntpwd;
528                                 }
529                         }
530                         p += 33; /* Move to the first character of the line after the NT password. */
531                 }
532
533                 DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
534                         user_name, uidval));
535
536                 if (*p == '[') {
537                         unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']');
538                         pw_buf->acct_ctrl = pdb_decode_acct_ctrl((char*)p);
539
540                         /* Must have some account type set. */
541                         if(pw_buf->acct_ctrl == 0) {
542                                 pw_buf->acct_ctrl = ACB_NORMAL;
543                         }
544
545                         /* Now try and get the last change time. */
546                         if(end_p) {
547                                 p = end_p + 1;
548                         }
549                         if(*p == ':') {
550                                 p++;
551                                 if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
552                                         int i;
553                                         p += 4;
554                                         for(i = 0; i < 8; i++) {
555                                                 if(p[i] == '\0' || !isxdigit(p[i])) {
556                                                         break;
557                                                 }
558                                         }
559                                         if(i == 8) {
560                                                 /*
561                                                  * p points at 8 characters of hex digits - 
562                                                  * read into a time_t as the seconds since
563                                                  * 1970 that the password was last changed.
564                                                  */
565                                                 pw_buf->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
566                                         }
567                                 }
568                         }
569                 } else {
570                         /* 'Old' style file. Fake up based on user name. */
571                         /*
572                          * Currently trust accounts are kept in the same
573                          * password file as 'normal accounts'. If this changes
574                          * we will have to fix this code. JRA.
575                          */
576                         if(pw_buf->smb_name[strlen(pw_buf->smb_name) - 1] == '$') {
577                                 pw_buf->acct_ctrl &= ~ACB_NORMAL;
578                                 pw_buf->acct_ctrl |= ACB_WSTRUST;
579                         }
580                 }
581
582                 return pw_buf;
583         }
584
585         DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
586         return NULL;
587 }
588
589 /************************************************************************
590  Create a new smbpasswd entry - malloced space returned.
591 *************************************************************************/
592
593 static char *format_new_smbpasswd_entry(const struct smb_passwd *newpwd)
594 {
595         int new_entry_length;
596         char *new_entry;
597         char *p;
598
599         new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + 
600                                 NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
601
602         if((new_entry = (char *)SMB_MALLOC( new_entry_length )) == NULL) {
603                 DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n",
604                         newpwd->smb_name ));
605                 return NULL;
606         }
607
608         slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
609
610         p = new_entry+strlen(new_entry);
611         pdb_sethexpwd(p, newpwd->smb_passwd, newpwd->acct_ctrl);
612         p+=strlen(p);
613         *p = ':';
614         p++;
615
616         pdb_sethexpwd(p, newpwd->smb_nt_passwd, newpwd->acct_ctrl);
617         p+=strlen(p);
618         *p = ':';
619         p++;
620
621         /* Add the account encoding and the last change time. */
622         slprintf((char *)p, new_entry_length - 1 - (p - new_entry),  "%s:LCT-%08X:\n",
623                 pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
624                 (uint32)newpwd->pass_last_set_time);
625
626         return new_entry;
627 }
628
629 /************************************************************************
630  Routine to add an entry to the smbpasswd file.
631 *************************************************************************/
632
633 static NTSTATUS add_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state,
634                                      struct smb_passwd *newpwd)
635 {
636         const char *pfile = smbpasswd_state->smbpasswd_file;
637         struct smb_passwd *pwd = NULL;
638         FILE *fp = NULL;
639         int wr_len;
640         int fd;
641         size_t new_entry_length;
642         char *new_entry;
643         SMB_OFF_T offpos;
644  
645         /* Open the smbpassword file - for update. */
646         fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth);
647
648         if (fp == NULL && errno == ENOENT) {
649                 /* Try again - create. */
650                 fp = startsmbfilepwent(pfile, PWF_CREATE, &smbpasswd_state->pw_file_lock_depth);
651         }
652
653         if (fp == NULL) {
654                 DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
655                 return map_nt_error_from_unix(errno);
656         }
657
658         /*
659          * Scan the file, a line at a time and check if the name matches.
660          */
661
662         while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
663                 if (strequal(newpwd->smb_name, pwd->smb_name)) {
664                         DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
665                         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
666                         return NT_STATUS_USER_EXISTS;
667                 }
668         }
669
670         /* Ok - entry doesn't exist. We can add it */
671
672         /* Create a new smb passwd entry and set it to the given password. */
673         /* 
674          * The add user write needs to be atomic - so get the fd from 
675          * the fp and do a raw write() call.
676          */
677         fd = fileno(fp);
678
679         if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) {
680                 NTSTATUS result = map_nt_error_from_unix(errno);
681                 DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
682 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
683                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
684                 return result;
685         }
686
687         if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) {
688                 DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
689 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
690                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
691                 return NT_STATUS_NO_MEMORY;
692         }
693
694         new_entry_length = strlen(new_entry);
695
696 #ifdef DEBUG_PASSWORD
697         DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|", 
698                         fd, (int)new_entry_length, new_entry));
699 #endif
700
701         if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) {
702                 NTSTATUS result = map_nt_error_from_unix(errno);
703                 DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
704 Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
705
706                 /* Remove the entry we just wrote. */
707                 if(sys_ftruncate(fd, offpos) == -1) {
708                         DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
709 Error was %s. Password file may be corrupt ! Please examine by hand !\n", 
710                                 newpwd->smb_name, strerror(errno)));
711                 }
712
713                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
714                 free(new_entry);
715                 return result;
716         }
717
718         free(new_entry);
719         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
720         return NT_STATUS_OK;
721 }
722
723 /************************************************************************
724  Routine to search the smbpasswd file for an entry matching the username.
725  and then modify its password entry. We can't use the startsmbpwent()/
726  getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
727  in the actual file to decide how much room we have to write data.
728  override = False, normal
729  override = True, override XXXXXXXX'd out password or NO PASS
730 ************************************************************************/
731
732 static BOOL mod_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const struct smb_passwd* pwd)
733 {
734         /* Static buffers we will return. */
735         pstring user_name;
736
737         char *status;
738         char linebuf[256];
739         char readbuf[1024];
740         int c;
741         fstring ascii_p16;
742         fstring encode_bits;
743         unsigned char *p = NULL;
744         size_t linebuf_len = 0;
745         FILE *fp;
746         int lockfd;
747         const char *pfile = smbpasswd_state->smbpasswd_file;
748         BOOL found_entry = False;
749         BOOL got_pass_last_set_time = False;
750
751         SMB_OFF_T pwd_seekpos = 0;
752
753         int i;
754         int wr_len;
755         int fd;
756
757         if (!*pfile) {
758                 DEBUG(0, ("No SMB password file set\n"));
759                 return False;
760         }
761         DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
762
763         fp = sys_fopen(pfile, "r+");
764
765         if (fp == NULL) {
766                 DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
767                 return False;
768         }
769         /* Set a buffer to do more efficient reads */
770         setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
771
772         lockfd = fileno(fp);
773
774         if (!pw_file_lock(lockfd, F_WRLCK, 5, &smbpasswd_state->pw_file_lock_depth)) {
775                 DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
776                 fclose(fp);
777                 return False;
778         }
779
780         /* Make sure it is only rw by the owner */
781         chmod(pfile, 0600);
782
783         /* We have a write lock on the file. */
784         /*
785          * Scan the file, a line at a time and check if the name matches.
786          */
787         status = linebuf;
788         while (status && !feof(fp)) {
789                 pwd_seekpos = sys_ftell(fp);
790
791                 linebuf[0] = '\0';
792
793                 status = fgets(linebuf, sizeof(linebuf), fp);
794                 if (status == NULL && ferror(fp)) {
795                         pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
796                         fclose(fp);
797                         return False;
798                 }
799
800                 /*
801                  * Check if the string is terminated with a newline - if not
802                  * then we must keep reading and discard until we get one.
803                  */
804                 linebuf_len = strlen(linebuf);
805                 if (linebuf[linebuf_len - 1] != '\n') {
806                         c = '\0';
807                         while (!ferror(fp) && !feof(fp)) {
808                                 c = fgetc(fp);
809                                 if (c == '\n') {
810                                         break;
811                                 }
812                         }
813                 } else {
814                         linebuf[linebuf_len - 1] = '\0';
815                 }
816
817 #ifdef DEBUG_PASSWORD
818                 DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
819 #endif
820
821                 if ((linebuf[0] == 0) && feof(fp)) {
822                         DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
823                         break;
824                 }
825
826                 /*
827                  * The line we have should be of the form :-
828                  * 
829                  * username:uid:[32hex bytes]:....other flags presently
830                  * ignored....
831                  * 
832                  * or,
833                  *
834                  * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
835                  *
836                  * if Windows NT compatible passwords are also present.
837                  */
838
839                 if (linebuf[0] == '#' || linebuf[0] == '\0') {
840                         DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
841                         continue;
842                 }
843
844                 p = (unsigned char *) strchr_m(linebuf, ':');
845
846                 if (p == NULL) {
847                         DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
848                         continue;
849                 }
850
851                 /*
852                  * As 256 is shorter than a pstring we don't need to check
853                  * length here - if this ever changes....
854                  */
855
856                 SMB_ASSERT(sizeof(user_name) > sizeof(linebuf));
857
858                 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
859                 user_name[PTR_DIFF(p, linebuf)] = '\0';
860                 if (strequal(user_name, pwd->smb_name)) {
861                         found_entry = True;
862                         break;
863                 }
864         }
865
866         if (!found_entry) {
867                 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
868                 fclose(fp);
869
870                 DEBUG(2, ("Cannot update entry for user %s, as they don't exist in the smbpasswd file!\n",
871                         pwd->smb_name));
872                 return False;
873         }
874
875         DEBUG(6, ("mod_smbfilepwd_entry: entry exists for user %s\n", pwd->smb_name));
876
877         /* User name matches - get uid and password */
878         p++; /* Go past ':' */
879
880         if (!isdigit(*p)) {
881                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (uid not number)\n",
882                         pwd->smb_name));
883                 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
884                 fclose(fp);
885                 return False;
886         }
887
888         while (*p && isdigit(*p)) {
889                 p++;
890         }
891         if (*p != ':') {
892                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no : after uid)\n",
893                         pwd->smb_name));
894                 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
895                 fclose(fp);
896                 return False;
897         }
898
899         /*
900          * Now get the password value - this should be 32 hex digits
901          * which are the ascii representations of a 16 byte string.
902          * Get two at a time and put them into the password.
903          */
904         p++;
905
906         /* Record exact password position */
907         pwd_seekpos += PTR_DIFF(p, linebuf);
908
909         if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
910                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
911                         pwd->smb_name));
912                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
913                 fclose(fp);
914                 return (False);
915         }
916
917         if (p[32] != ':') {
918                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
919                         pwd->smb_name));
920                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
921                 fclose(fp);
922                 return False;
923         }
924
925         /* Now check if the NT compatible password is available. */
926         p += 33; /* Move to the first character of the line after the lanman password. */
927         if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
928                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
929                         pwd->smb_name));
930                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
931                 fclose(fp);
932                 return (False);
933         }
934
935         if (p[32] != ':') {
936                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
937                         pwd->smb_name));
938                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
939                 fclose(fp);
940                 return False;
941         }
942
943         /* 
944          * Now check if the account info and the password last
945          * change time is available.
946          */
947         p += 33; /* Move to the first character of the line after the NT password. */
948
949         if (*p == '[') {
950                 i = 0;
951                 encode_bits[i++] = *p++;
952                 while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']')) {
953                         encode_bits[i++] = *p++;
954                 }
955
956                 encode_bits[i++] = ']';
957                 encode_bits[i++] = '\0';
958
959                 if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
960                         /*
961                          * We are using a new format, space padded
962                          * acct ctrl field. Encode the given acct ctrl
963                          * bits into it.
964                          */
965                         fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
966                 } else {
967                         DEBUG(0,("mod_smbfilepwd_entry:  Using old smbpasswd format for user %s. \
968 This is no longer supported.!\n", pwd->smb_name));
969                         DEBUG(0,("mod_smbfilepwd_entry:  No changes made, failing.!\n"));
970                         pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
971                         fclose(fp);
972                         return False;
973                 }
974
975                 /* Go past the ']' */
976                 if(linebuf_len > PTR_DIFF(p, linebuf)) {
977                         p++;
978                 }
979
980                 if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
981                         p++;
982
983                         /* We should be pointing at the LCT entry. */
984                         if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
985                                 p += 4;
986                                 for(i = 0; i < 8; i++) {
987                                         if(p[i] == '\0' || !isxdigit(p[i])) {
988                                                 break;
989                                         }
990                                 }
991                                 if(i == 8) {
992                                         /*
993                                          * p points at 8 characters of hex digits -
994                                          * read into a time_t as the seconds since
995                                          * 1970 that the password was last changed.
996                                          */
997                                         got_pass_last_set_time = True;
998                                 } /* i == 8 */
999                         } /* *p && StrnCaseCmp() */
1000                 } /* p == ':' */
1001         } /* p == '[' */
1002
1003         /* Entry is correctly formed. */
1004
1005         /* Create the 32 byte representation of the new p16 */
1006         pdb_sethexpwd(ascii_p16, pwd->smb_passwd, pwd->acct_ctrl);
1007
1008         /* Add on the NT md4 hash */
1009         ascii_p16[32] = ':';
1010         wr_len = 66;
1011         pdb_sethexpwd(ascii_p16+33, pwd->smb_nt_passwd, pwd->acct_ctrl);
1012         ascii_p16[65] = ':';
1013         ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
1014
1015         /* Add on the account info bits and the time of last password change. */
1016         if(got_pass_last_set_time) {
1017                 slprintf(&ascii_p16[strlen(ascii_p16)], 
1018                         sizeof(ascii_p16)-(strlen(ascii_p16)+1),
1019                         "%s:LCT-%08X:", 
1020                         encode_bits, (uint32)pwd->pass_last_set_time );
1021                 wr_len = strlen(ascii_p16);
1022         }
1023
1024 #ifdef DEBUG_PASSWORD
1025         DEBUG(100,("mod_smbfilepwd_entry: "));
1026         dump_data(100, (uint8 *)ascii_p16, wr_len);
1027 #endif
1028
1029         if(wr_len > sizeof(linebuf)) {
1030                 DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
1031                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1032                 fclose(fp);
1033                 return (False);
1034         }
1035
1036         /*
1037          * Do an atomic write into the file at the position defined by
1038          * seekpos.
1039          */
1040
1041         /* The mod user write needs to be atomic - so get the fd from 
1042                 the fp and do a raw write() call.
1043          */
1044
1045         fd = fileno(fp);
1046
1047         if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
1048                 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1049                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1050                 fclose(fp);
1051                 return False;
1052         }
1053
1054         /* Sanity check - ensure the areas we are writing are framed by ':' */
1055         if (read(fd, linebuf, wr_len+1) != wr_len+1) {
1056                 DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
1057                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1058                 fclose(fp);
1059                 return False;
1060         }
1061
1062         if ((linebuf[0] != ':') || (linebuf[wr_len] != ':'))    {
1063                 DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
1064                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1065                 fclose(fp);
1066                 return False;
1067         }
1068  
1069         if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
1070                 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1071                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1072                 fclose(fp);
1073                 return False;
1074         }
1075
1076         if (write(fd, ascii_p16, wr_len) != wr_len) {
1077                 DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
1078                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1079                 fclose(fp);
1080                 return False;
1081         }
1082
1083         pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1084         fclose(fp);
1085         return True;
1086 }
1087
1088 /************************************************************************
1089  Routine to delete an entry in the smbpasswd file by name.
1090 *************************************************************************/
1091
1092 static BOOL del_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const char *name)
1093 {
1094         const char *pfile = smbpasswd_state->smbpasswd_file;
1095         pstring pfile2;
1096         struct smb_passwd *pwd = NULL;
1097         FILE *fp = NULL;
1098         FILE *fp_write = NULL;
1099         int pfile2_lockdepth = 0;
1100
1101         slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)sys_getpid() );
1102
1103         /*
1104          * Open the smbpassword file - for update. It needs to be update
1105          * as we need any other processes to wait until we have replaced
1106          * it.
1107          */
1108
1109         if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth)) == NULL) {
1110                 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1111                 return False;
1112         }
1113
1114         /*
1115          * Create the replacement password file.
1116          */
1117         if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
1118                 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1119                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1120                 return False;
1121         }
1122
1123         /*
1124          * Scan the file, a line at a time and check if the name matches.
1125          */
1126
1127         while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
1128                 char *new_entry;
1129                 size_t new_entry_length;
1130
1131                 if (strequal(name, pwd->smb_name)) {
1132                         DEBUG(10, ("del_smbfilepwd_entry: found entry with "
1133                                    "name %s - deleting it.\n", name));
1134                         continue;
1135                 }
1136
1137                 /*
1138                  * We need to copy the entry out into the second file.
1139                  */
1140
1141                 if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) {
1142                         DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
1143 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1144                         unlink(pfile2);
1145                         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1146                         endsmbfilepwent(fp_write, &pfile2_lockdepth);
1147                         return False;
1148                 }
1149
1150                 new_entry_length = strlen(new_entry);
1151
1152                 if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) {
1153                         DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
1154 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1155                         unlink(pfile2);
1156                         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1157                         endsmbfilepwent(fp_write, &pfile2_lockdepth);
1158                         free(new_entry);
1159                         return False;
1160                 }
1161
1162                 free(new_entry);
1163         }
1164
1165         /*
1166          * Ensure pfile2 is flushed before rename.
1167          */
1168
1169         if(fflush(fp_write) != 0) {
1170                 DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
1171                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1172                 endsmbfilepwent(fp_write,&pfile2_lockdepth);
1173                 return False;
1174         }
1175
1176         /*
1177          * Do an atomic rename - then release the locks.
1178          */
1179
1180         if(rename(pfile2,pfile) != 0) {
1181                 unlink(pfile2);
1182         }
1183   
1184         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1185         endsmbfilepwent(fp_write,&pfile2_lockdepth);
1186         return True;
1187 }
1188
1189 /*********************************************************************
1190  Create a smb_passwd struct from a struct samu.
1191  We will not allocate any new memory.  The smb_passwd struct
1192  should only stay around as long as the struct samu does.
1193  ********************************************************************/
1194
1195 static BOOL build_smb_pass (struct smb_passwd *smb_pw, const struct samu *sampass)
1196 {
1197         uint32 rid;
1198
1199         if (sampass == NULL) 
1200                 return False;
1201         ZERO_STRUCTP(smb_pw);
1202
1203         if (!IS_SAM_DEFAULT(sampass, PDB_USERSID)) {
1204                 rid = pdb_get_user_rid(sampass);
1205                 
1206                 /* If the user specified a RID, make sure its able to be both stored and retreived */
1207                 if (rid == DOMAIN_USER_RID_GUEST) {
1208                         struct passwd *passwd = getpwnam_alloc(NULL, lp_guestaccount());
1209                         if (!passwd) {
1210                                 DEBUG(0, ("Could not find guest account via getpwnam()! (%s)\n", lp_guestaccount()));
1211                                 return False;
1212                         }
1213                         smb_pw->smb_userid=passwd->pw_uid;
1214                         TALLOC_FREE(passwd);
1215                 } else if (algorithmic_pdb_rid_is_user(rid)) {
1216                         smb_pw->smb_userid=algorithmic_pdb_user_rid_to_uid(rid);
1217                 } else {
1218                         DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
1219                         return False;
1220                 }
1221         }
1222
1223         smb_pw->smb_name=(const char*)pdb_get_username(sampass);
1224
1225         smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
1226         smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
1227
1228         smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
1229         smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
1230
1231         return True;
1232 }       
1233
1234 /*********************************************************************
1235  Create a struct samu from a smb_passwd struct
1236  ********************************************************************/
1237
1238 static BOOL build_sam_account(struct smbpasswd_privates *smbpasswd_state, 
1239                               struct samu *sam_pass, const struct smb_passwd *pw_buf)
1240 {
1241         struct passwd *pwfile;
1242         
1243         if ( !sam_pass ) {
1244                 DEBUG(5,("build_sam_account: struct samu is NULL\n"));
1245                 return False;
1246         }
1247
1248         /* verify the user account exists */
1249
1250         if ( !(pwfile = Get_Pwnam_alloc(NULL, pw_buf->smb_name )) ) {
1251                 DEBUG(0,("build_sam_account: smbpasswd database is corrupt!  username %s with uid "
1252                 "%u is not in unix passwd database!\n", pw_buf->smb_name, pw_buf->smb_userid));
1253                         return False;
1254         }
1255         
1256         if ( !NT_STATUS_IS_OK( samu_set_unix(sam_pass, pwfile )) )
1257                 return False;
1258                 
1259         TALLOC_FREE(pwfile);
1260
1261         /* set remaining fields */
1262                 
1263         pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd, PDB_SET);
1264         pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd, PDB_SET);                  
1265         pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl, PDB_SET);
1266         pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1267         pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1268         
1269         return True;
1270 }
1271
1272 /*****************************************************************
1273  Functions to be implemented by the new passdb API 
1274  ****************************************************************/
1275
1276 static NTSTATUS smbpasswd_setsampwent (struct pdb_methods *my_methods, BOOL update, uint32 acb_mask)
1277 {
1278         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1279         
1280         smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
1281                                                        update ? PWF_UPDATE : PWF_READ, 
1282                                                        &(smbpasswd_state->pw_file_lock_depth));
1283                                    
1284         /* did we fail?  Should we try to create it? */
1285         if (!smbpasswd_state->pw_file && update && errno == ENOENT) {
1286                 FILE *fp;
1287                 /* slprintf(msg_str,msg_str_len-1,
1288                    "smbpasswd file did not exist - attempting to create it.\n"); */
1289                 DEBUG(0,("smbpasswd file did not exist - attempting to create it.\n"));
1290                 fp = sys_fopen(smbpasswd_state->smbpasswd_file, "w");
1291                 if (fp) {
1292                         fprintf(fp, "# Samba SMB password file\n");
1293                         fclose(fp);
1294                 }
1295                 
1296                 smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
1297                                                              update ? PWF_UPDATE : PWF_READ, 
1298                                                              &(smbpasswd_state->pw_file_lock_depth));
1299         }
1300         
1301         if (smbpasswd_state->pw_file != NULL)
1302                 return NT_STATUS_OK;
1303         else
1304                 return NT_STATUS_UNSUCCESSFUL;  
1305 }
1306
1307 static void smbpasswd_endsampwent (struct pdb_methods *my_methods)
1308 {
1309         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1310         endsmbfilepwent(smbpasswd_state->pw_file, &(smbpasswd_state->pw_file_lock_depth));
1311 }
1312  
1313 /*****************************************************************
1314  ****************************************************************/
1315
1316 static NTSTATUS smbpasswd_getsampwent(struct pdb_methods *my_methods, struct samu *user)
1317 {
1318         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1319         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1320         struct smb_passwd *pw_buf=NULL;
1321         BOOL done = False;
1322
1323         DEBUG(5,("pdb_getsampwent\n"));
1324
1325         if ( !user ) {
1326                 DEBUG(5,("pdb_getsampwent (smbpasswd): user is NULL\n"));
1327                 return nt_status;
1328         }
1329
1330         while (!done) {
1331                 /* do we have an entry? */
1332                 pw_buf = getsmbfilepwent(smbpasswd_state, smbpasswd_state->pw_file);
1333                 if (pw_buf == NULL) 
1334                         return nt_status;
1335
1336                 /* build the struct samu entry from the smb_passwd struct. 
1337                    We loop in case the user in the pdb does not exist in 
1338                    the local system password file */
1339                 if (build_sam_account(smbpasswd_state, user, pw_buf))
1340                         done = True;
1341         }
1342
1343         DEBUG(5,("getsampwent (smbpasswd): done\n"));
1344
1345         /* success */
1346         return NT_STATUS_OK;
1347 }
1348
1349 /****************************************************************
1350  Search smbpasswd file by iterating over the entries.  Do not
1351  call getpwnam() for unix account information until we have found
1352  the correct entry
1353  ***************************************************************/
1354
1355 static NTSTATUS smbpasswd_getsampwnam(struct pdb_methods *my_methods, 
1356                                   struct samu *sam_acct, const char *username)
1357 {
1358         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1359         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1360         struct smb_passwd *smb_pw;
1361         FILE *fp = NULL;
1362
1363         DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
1364
1365         /* startsmbfilepwent() is used here as we don't want to lookup
1366            the UNIX account in the local system password file until
1367            we have a match.  */
1368         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1369
1370         if (fp == NULL) {
1371                 DEBUG(0, ("Unable to open passdb database.\n"));
1372                 return nt_status;
1373         }
1374
1375         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
1376                 /* do nothing....another loop */ ;
1377         
1378         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1379
1380
1381         /* did we locate the username in smbpasswd  */
1382         if (smb_pw == NULL)
1383                 return nt_status;
1384         
1385         DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1386
1387         if (!sam_acct) {
1388                 DEBUG(10,("getsampwnam (smbpasswd): struct samu is NULL\n"));
1389                 return nt_status;
1390         }
1391                 
1392         /* now build the struct samu */
1393         if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
1394                 return nt_status;
1395
1396         /* success */
1397         return NT_STATUS_OK;
1398 }
1399
1400 static NTSTATUS smbpasswd_getsampwsid(struct pdb_methods *my_methods, struct samu *sam_acct, const DOM_SID *sid)
1401 {
1402         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1403         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1404         struct smb_passwd *smb_pw;
1405         FILE *fp = NULL;
1406         fstring sid_str;
1407         uint32 rid;
1408         
1409         DEBUG(10, ("smbpasswd_getsampwrid: search by sid: %s\n", sid_to_string(sid_str, sid)));
1410
1411         if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
1412                 return NT_STATUS_UNSUCCESSFUL;
1413
1414         /* More special case 'guest account' hacks... */
1415         if (rid == DOMAIN_USER_RID_GUEST) {
1416                 const char *guest_account = lp_guestaccount();
1417                 if (!(guest_account && *guest_account)) {
1418                         DEBUG(1, ("Guest account not specfied!\n"));
1419                         return nt_status;
1420                 }
1421                 return smbpasswd_getsampwnam(my_methods, sam_acct, guest_account);
1422         }
1423
1424         /* Open the sam password file - not for update. */
1425         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1426
1427         if (fp == NULL) {
1428                 DEBUG(0, ("Unable to open passdb database.\n"));
1429                 return nt_status;
1430         }
1431
1432         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (algorithmic_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
1433                 /* do nothing */ ;
1434
1435         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1436
1437
1438         /* did we locate the username in smbpasswd  */
1439         if (smb_pw == NULL)
1440                 return nt_status;
1441         
1442         DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1443                 
1444         if (!sam_acct) {
1445                 DEBUG(10,("getsampwrid: (smbpasswd) struct samu is NULL\n"));
1446                 return nt_status;
1447         }
1448
1449         /* now build the struct samu */
1450         if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
1451                 return nt_status;
1452
1453         /* build_sam_account might change the SID on us, if the name was for the guest account */
1454         if (NT_STATUS_IS_OK(nt_status) && !sid_equal(pdb_get_user_sid(sam_acct), sid)) {
1455                 fstring sid_string1, sid_string2;
1456                 DEBUG(1, ("looking for user with sid %s instead returned %s for account %s!?!\n",
1457                           sid_to_string(sid_string1, sid), sid_to_string(sid_string2, pdb_get_user_sid(sam_acct)), pdb_get_username(sam_acct)));
1458                 return NT_STATUS_NO_SUCH_USER;
1459         }
1460
1461         /* success */
1462         return NT_STATUS_OK;
1463 }
1464
1465 static NTSTATUS smbpasswd_add_sam_account(struct pdb_methods *my_methods, struct samu *sampass)
1466 {
1467         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1468         struct smb_passwd smb_pw;
1469         
1470         /* convert the struct samu */
1471         if (!build_smb_pass(&smb_pw, sampass)) {
1472                 return NT_STATUS_UNSUCCESSFUL;
1473         }
1474         
1475         /* add the entry */
1476         return add_smbfilepwd_entry(smbpasswd_state, &smb_pw);
1477 }
1478
1479 static NTSTATUS smbpasswd_update_sam_account(struct pdb_methods *my_methods, struct samu *sampass)
1480 {
1481         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1482         struct smb_passwd smb_pw;
1483         
1484         /* convert the struct samu */
1485         if (!build_smb_pass(&smb_pw, sampass)) {
1486                 DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
1487                 return NT_STATUS_UNSUCCESSFUL;
1488         }
1489         
1490         /* update the entry */
1491         if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1492                 DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
1493                 return NT_STATUS_UNSUCCESSFUL;
1494         }
1495         
1496         return NT_STATUS_OK;
1497 }
1498
1499 static NTSTATUS smbpasswd_delete_sam_account (struct pdb_methods *my_methods, struct samu *sampass)
1500 {
1501         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1502
1503         const char *username = pdb_get_username(sampass);
1504
1505         if (del_smbfilepwd_entry(smbpasswd_state, username))
1506                 return NT_STATUS_OK;
1507
1508         return NT_STATUS_UNSUCCESSFUL;
1509 }
1510
1511 static NTSTATUS smbpasswd_rename_sam_account (struct pdb_methods *my_methods, 
1512                                               struct samu *old_acct,
1513                                               const char *newname)
1514 {
1515         pstring rename_script;
1516         struct samu *new_acct = NULL;
1517         BOOL interim_account = False;
1518         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
1519
1520         if (!*(lp_renameuser_script()))
1521                 goto done;
1522
1523         if ( !(new_acct = samu_new( NULL )) ) {
1524                 return NT_STATUS_NO_MEMORY;
1525         }
1526         
1527         if ( !pdb_copy_sam_account( new_acct, old_acct ) 
1528                 || !pdb_set_username(new_acct, newname, PDB_CHANGED)) 
1529         {
1530                 goto done;
1531         }
1532         
1533         ret = smbpasswd_add_sam_account(my_methods, new_acct);
1534         if (!NT_STATUS_IS_OK(ret))
1535                 goto done;
1536
1537         interim_account = True;
1538
1539         /* rename the posix user */
1540         pstrcpy(rename_script, lp_renameuser_script());
1541
1542         if (*rename_script) {
1543                 int rename_ret;
1544
1545                 string_sub2(rename_script, "%unew", newname, sizeof(pstring), 
1546                             True, False, True);
1547                 string_sub2(rename_script, "%uold", pdb_get_username(old_acct), 
1548                             sizeof(pstring), True, False, True);
1549
1550                 rename_ret = smbrun(rename_script, NULL);
1551
1552                 DEBUG(rename_ret ? 0 : 3,("Running the command `%s' gave %d\n", rename_script, rename_ret));
1553
1554                 if (rename_ret == 0) {
1555                         smb_nscd_flush_user_cache();
1556                 }
1557
1558                 if (rename_ret) 
1559                         goto done; 
1560         } else {
1561                 goto done;
1562         }
1563
1564         smbpasswd_delete_sam_account(my_methods, old_acct);
1565         interim_account = False;
1566
1567 done:   
1568         /* cleanup */
1569         if (interim_account)
1570                 smbpasswd_delete_sam_account(my_methods, new_acct);
1571
1572         if (new_acct)
1573                 TALLOC_FREE(new_acct);
1574         
1575         return (ret);   
1576 }
1577
1578 static BOOL smbpasswd_rid_algorithm(struct pdb_methods *methods)
1579 {
1580         return True;
1581 }
1582
1583 static void free_private_data(void **vp) 
1584 {
1585         struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
1586         
1587         endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
1588         
1589         *privates = NULL;
1590         /* No need to free any further, as it is talloc()ed */
1591 }
1592
1593 static NTSTATUS pdb_init_smbpasswd( struct pdb_methods **pdb_method, const char *location )
1594 {
1595         NTSTATUS nt_status;
1596         struct smbpasswd_privates *privates;
1597
1598         if ( !NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method )) ) {
1599                 return nt_status;
1600         }
1601
1602         (*pdb_method)->name = "smbpasswd";
1603
1604         (*pdb_method)->setsampwent = smbpasswd_setsampwent;
1605         (*pdb_method)->endsampwent = smbpasswd_endsampwent;
1606         (*pdb_method)->getsampwent = smbpasswd_getsampwent;
1607         (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
1608         (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
1609         (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
1610         (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
1611         (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
1612         (*pdb_method)->rename_sam_account = smbpasswd_rename_sam_account;
1613
1614         (*pdb_method)->rid_algorithm = smbpasswd_rid_algorithm;
1615
1616         /* Setup private data and free function */
1617
1618         if ( !(privates = TALLOC_ZERO_P( *pdb_method, struct smbpasswd_privates )) ) {
1619                 DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
1620                 return NT_STATUS_NO_MEMORY;
1621         }
1622
1623         /* Store some config details */
1624
1625         if (location) {
1626                 privates->smbpasswd_file = talloc_strdup(*pdb_method, location);
1627         } else {
1628                 privates->smbpasswd_file = talloc_strdup(*pdb_method, lp_smb_passwd_file());
1629         }
1630         
1631         if (!privates->smbpasswd_file) {
1632                 DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
1633                 return NT_STATUS_NO_MEMORY;
1634         }
1635
1636         (*pdb_method)->private_data = privates;
1637
1638         (*pdb_method)->free_private_data = free_private_data;
1639
1640         return NT_STATUS_OK;
1641 }
1642
1643 NTSTATUS pdb_smbpasswd_init(void) 
1644 {
1645         return smb_register_passdb(PASSDB_INTERFACE_VERSION, "smbpasswd", pdb_init_smbpasswd);
1646 }