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