Put strcasecmp/strncasecmp on the banned list (except for needed calls
[amitay/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             return False;
902     }
903
904     /* Go past the ']' */
905     if(linebuf_len > PTR_DIFF(p, linebuf))
906       p++;
907
908     if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
909       p++;
910
911       /* We should be pointing at the LCT entry. */
912       if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
913
914         p += 4;
915         for(i = 0; i < 8; i++) {
916           if(p[i] == '\0' || !isxdigit(p[i]))
917             break;
918         }
919         if(i == 8) {
920           /*
921            * p points at 8 characters of hex digits -
922            * read into a time_t as the seconds since
923            * 1970 that the password was last changed.
924            */
925           got_pass_last_set_time = True;
926         } /* i == 8 */
927       } /* *p && StrnCaseCmp() */
928     } /* p == ':' */
929   } /* p == '[' */
930
931   /* Entry is correctly formed. */
932
933   /* Create the 32 byte representation of the new p16 */
934   pdb_sethexpwd(ascii_p16, pwd->smb_passwd, pwd->acct_ctrl);
935
936   /* Add on the NT md4 hash */
937   ascii_p16[32] = ':';
938   wr_len = 66;
939   pdb_sethexpwd(ascii_p16+33, pwd->smb_nt_passwd, pwd->acct_ctrl);
940   ascii_p16[65] = ':';
941   ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
942
943   /* Add on the account info bits and the time of last
944      password change. */
945
946   if(got_pass_last_set_time) {
947     slprintf(&ascii_p16[strlen(ascii_p16)], 
948              sizeof(ascii_p16)-(strlen(ascii_p16)+1),
949              "%s:LCT-%08X:", 
950                      encode_bits, (uint32)pwd->pass_last_set_time );
951     wr_len = strlen(ascii_p16);
952   }
953
954 #ifdef DEBUG_PASSWORD
955   DEBUG(100,("mod_smbfilepwd_entry: "));
956   dump_data(100, ascii_p16, wr_len);
957 #endif
958
959   if(wr_len > sizeof(linebuf)) {
960     DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
961     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
962     fclose(fp);
963     return (False);
964   }
965
966   /*
967    * Do an atomic write into the file at the position defined by
968    * seekpos.
969    */
970
971   /* The mod user write needs to be atomic - so get the fd from 
972      the fp and do a raw write() call.
973    */
974
975   fd = fileno(fp);
976
977   if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
978     DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
979     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
980     fclose(fp);
981     return False;
982   }
983
984   /* Sanity check - ensure the areas we are writing are framed by ':' */
985   if (read(fd, linebuf, wr_len+1) != wr_len+1) {
986     DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
987     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
988     fclose(fp);
989     return False;
990   }
991
992   if ((linebuf[0] != ':') || (linebuf[wr_len] != ':'))  {
993     DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
994     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
995     fclose(fp);
996     return False;
997   }
998  
999   if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
1000     DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1001     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
1002     fclose(fp);
1003     return False;
1004   }
1005
1006   if (write(fd, ascii_p16, wr_len) != wr_len) {
1007     DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
1008     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
1009     fclose(fp);
1010     return False;
1011   }
1012
1013   pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
1014   fclose(fp);
1015   return True;
1016 }
1017
1018 /************************************************************************
1019  Routine to delete an entry in the smbpasswd file by name.
1020 *************************************************************************/
1021
1022 static BOOL del_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const char *name)
1023 {
1024         const char *pfile = smbpasswd_state->smbpasswd_file;
1025   pstring pfile2;
1026   struct smb_passwd *pwd = NULL;
1027   FILE *fp = NULL;
1028   FILE *fp_write = NULL;
1029   int pfile2_lockdepth = 0;
1030
1031   slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)sys_getpid() );
1032
1033   /*
1034    * Open the smbpassword file - for update. It needs to be update
1035    * as we need any other processes to wait until we have replaced
1036    * it.
1037    */
1038
1039   if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &(smbpasswd_state->pw_file_lock_depth))) == NULL) {
1040     DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1041     return False;
1042   }
1043
1044   /*
1045    * Create the replacement password file.
1046    */
1047   if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
1048     DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1049     endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1050     return False;
1051   }
1052
1053   /*
1054    * Scan the file, a line at a time and check if the name matches.
1055    */
1056
1057   while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
1058     char *new_entry;
1059     size_t new_entry_length;
1060
1061     if (strequal(name, pwd->smb_name)) {
1062       DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name));
1063       continue;
1064     }
1065
1066     /*
1067      * We need to copy the entry out into the second file.
1068      */
1069
1070     if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) 
1071     {
1072         DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
1073 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1074         unlink(pfile2);
1075         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1076         endsmbfilepwent(fp_write, &pfile2_lockdepth);
1077         return False;
1078     }
1079
1080     new_entry_length = strlen(new_entry);
1081
1082     if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) 
1083     {
1084         DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
1085 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1086         unlink(pfile2);
1087         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1088         endsmbfilepwent(fp_write, &pfile2_lockdepth);
1089         free(new_entry);
1090         return False;
1091     }
1092
1093     free(new_entry);
1094   }
1095
1096   /*
1097    * Ensure pfile2 is flushed before rename.
1098    */
1099
1100   if(fflush(fp_write) != 0) 
1101   {
1102         DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
1103         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1104         endsmbfilepwent(fp_write,&pfile2_lockdepth);
1105         return False;
1106   }
1107
1108   /*
1109    * Do an atomic rename - then release the locks.
1110    */
1111
1112   if(rename(pfile2,pfile) != 0) {
1113     unlink(pfile2);
1114   }
1115   
1116   endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1117   endsmbfilepwent(fp_write,&pfile2_lockdepth);
1118   return True;
1119 }
1120
1121 /*********************************************************************
1122  Create a smb_passwd struct from a SAM_ACCOUNT.
1123  We will not allocate any new memory.  The smb_passwd struct
1124  should only stay around as long as the SAM_ACCOUNT does.
1125  ********************************************************************/
1126 static BOOL build_smb_pass (struct smb_passwd *smb_pw, const SAM_ACCOUNT *sampass)
1127 {
1128         uint32 rid;
1129
1130         if (sampass == NULL) 
1131                 return False;
1132         ZERO_STRUCTP(smb_pw);
1133
1134         if (!IS_SAM_DEFAULT(sampass, PDB_USERSID)) {
1135                 rid = pdb_get_user_rid(sampass);
1136                 
1137                 /* If the user specified a RID, make sure its able to be both stored and retreived */
1138                 if (rid == DOMAIN_USER_RID_GUEST) {
1139                         struct passwd *passwd = getpwnam_alloc(lp_guestaccount());
1140                         if (!passwd) {
1141                                 DEBUG(0, ("Could not find gest account via getpwnam()! (%s)\n", lp_guestaccount()));
1142                                 return False;
1143                         }
1144                         smb_pw->smb_userid=passwd->pw_uid;
1145                         passwd_free(&passwd);
1146
1147                 } else if (fallback_pdb_rid_is_user(rid)) {
1148                         smb_pw->smb_userid=fallback_pdb_user_rid_to_uid(rid);
1149                 } else {
1150                         DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
1151                         return False;
1152                 }
1153         }
1154
1155         smb_pw->smb_name=(const char*)pdb_get_username(sampass);
1156
1157         smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
1158         smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
1159
1160         smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
1161         smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
1162
1163         return True;
1164 }       
1165
1166 /*********************************************************************
1167  Create a SAM_ACCOUNT from a smb_passwd struct
1168  ********************************************************************/
1169 static BOOL build_sam_account(struct smbpasswd_privates *smbpasswd_state, 
1170                               SAM_ACCOUNT *sam_pass, const struct smb_passwd *pw_buf)
1171 {
1172         struct passwd *pwfile;
1173         
1174         if (sam_pass==NULL) {
1175                 DEBUG(5,("build_sam_account: SAM_ACCOUNT is NULL\n"));
1176                 return False;
1177         }
1178
1179         /* verify the user account exists */
1180                         
1181         if ( !(pwfile = getpwnam_alloc(pw_buf->smb_name)) ) {
1182                 DEBUG(0,("build_sam_account: smbpasswd database is corrupt!  username %s with uid "
1183                 "%u is not in unix passwd database!\n", pw_buf->smb_name, pw_buf->smb_userid));
1184                         return False;
1185         }
1186         
1187         if (!NT_STATUS_IS_OK(pdb_fill_sam_pw(sam_pass, pwfile)))
1188                 return False;
1189                 
1190         passwd_free(&pwfile);
1191
1192         /* set remaining fields */
1193                 
1194         pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd, PDB_SET);
1195         pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd, PDB_SET);                  
1196         pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl, PDB_SET);
1197         pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1198         pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1199         
1200         return True;
1201 }
1202
1203 /*****************************************************************
1204  Functions to be implemented by the new passdb API 
1205  ****************************************************************/
1206 static NTSTATUS smbpasswd_setsampwent (struct pdb_methods *my_methods, BOOL update)
1207 {
1208         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1209         
1210         smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
1211                                                        update ? PWF_UPDATE : PWF_READ, 
1212                                                        &(smbpasswd_state->pw_file_lock_depth));
1213                                    
1214         /* did we fail?  Should we try to create it? */
1215         if (!smbpasswd_state->pw_file && update && errno == ENOENT) 
1216         {
1217                 FILE *fp;
1218                 /* slprintf(msg_str,msg_str_len-1,
1219                    "smbpasswd file did not exist - attempting to create it.\n"); */
1220                 DEBUG(0,("smbpasswd file did not exist - attempting to create it.\n"));
1221                 fp = sys_fopen(smbpasswd_state->smbpasswd_file, "w");
1222                 if (fp) 
1223                 {
1224                         fprintf(fp, "# Samba SMB password file\n");
1225                         fclose(fp);
1226                 }
1227                 
1228                 smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
1229                                                              update ? PWF_UPDATE : PWF_READ, 
1230                                                              &(smbpasswd_state->pw_file_lock_depth));
1231         }
1232         
1233         if (smbpasswd_state->pw_file != NULL)
1234                 return NT_STATUS_OK;
1235         else
1236                 return NT_STATUS_UNSUCCESSFUL;  
1237 }
1238
1239 static void smbpasswd_endsampwent (struct pdb_methods *my_methods)
1240 {
1241         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1242         endsmbfilepwent(smbpasswd_state->pw_file, &(smbpasswd_state->pw_file_lock_depth));
1243 }
1244  
1245 /*****************************************************************
1246  ****************************************************************/
1247 static NTSTATUS smbpasswd_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
1248 {
1249         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1250         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1251         struct smb_passwd *pw_buf=NULL;
1252         BOOL done = False;
1253         DEBUG(5,("pdb_getsampwent\n"));
1254
1255         if (user==NULL) {
1256                 DEBUG(5,("pdb_getsampwent (smbpasswd): user is NULL\n"));
1257 #if 0
1258                 smb_panic("NULL pointer passed to getsampwent (smbpasswd)\n");
1259 #endif
1260                 return nt_status;
1261         }
1262
1263         while (!done)
1264         {
1265                 /* do we have an entry? */
1266                 pw_buf = getsmbfilepwent(smbpasswd_state, smbpasswd_state->pw_file);
1267                 if (pw_buf == NULL) 
1268                         return nt_status;
1269
1270                 /* build the SAM_ACCOUNT entry from the smb_passwd struct. 
1271                    We loop in case the user in the pdb does not exist in 
1272                    the local system password file */
1273                 if (build_sam_account(smbpasswd_state, user, pw_buf))
1274                         done = True;
1275         }
1276
1277         DEBUG(5,("getsampwent (smbpasswd): done\n"));
1278
1279         /* success */
1280         return NT_STATUS_OK;
1281 }
1282
1283
1284 /****************************************************************
1285  Search smbpasswd file by iterating over the entries.  Do not
1286  call getpwnam() for unix account information until we have found
1287  the correct entry
1288  ***************************************************************/
1289 static NTSTATUS smbpasswd_getsampwnam(struct pdb_methods *my_methods, 
1290                                   SAM_ACCOUNT *sam_acct, const char *username)
1291 {
1292         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1293         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1294         struct smb_passwd *smb_pw;
1295         void *fp = NULL;
1296
1297         DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
1298
1299         /* startsmbfilepwent() is used here as we don't want to lookup
1300            the UNIX account in the local system password file until
1301            we have a match.  */
1302         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1303
1304         if (fp == NULL) {
1305                 DEBUG(0, ("Unable to open passdb database.\n"));
1306                 return nt_status;
1307         }
1308
1309         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
1310                 /* do nothing....another loop */ ;
1311         
1312         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1313
1314
1315         /* did we locate the username in smbpasswd  */
1316         if (smb_pw == NULL)
1317                 return nt_status;
1318         
1319         DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1320
1321         if (!sam_acct) {
1322                 DEBUG(10,("getsampwnam (smbpasswd): SAM_ACCOUNT is NULL\n"));
1323 #if 0
1324                 smb_panic("NULL pointer passed to pdb_getsampwnam\n");
1325 #endif
1326                 return nt_status;
1327         }
1328                 
1329         /* now build the SAM_ACCOUNT */
1330         if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
1331                 return nt_status;
1332
1333         /* success */
1334         return NT_STATUS_OK;
1335 }
1336
1337 static NTSTATUS smbpasswd_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT *sam_acct, const DOM_SID *sid)
1338 {
1339         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1340         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1341         struct smb_passwd *smb_pw;
1342         void *fp = NULL;
1343         fstring sid_str;
1344         uint32 rid;
1345         
1346         DEBUG(10, ("smbpasswd_getsampwrid: search by sid: %s\n", sid_to_string(sid_str, sid)));
1347
1348         if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
1349                 return NT_STATUS_UNSUCCESSFUL;
1350
1351         /* More special case 'guest account' hacks... */
1352         if (rid == DOMAIN_USER_RID_GUEST) {
1353                 const char *guest_account = lp_guestaccount();
1354                 if (!(guest_account && *guest_account)) {
1355                         DEBUG(1, ("Guest account not specfied!\n"));
1356                         return nt_status;
1357                 }
1358                 return smbpasswd_getsampwnam(my_methods, sam_acct, guest_account);
1359         }
1360
1361         /* Open the sam password file - not for update. */
1362         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1363
1364         if (fp == NULL) {
1365                 DEBUG(0, ("Unable to open passdb database.\n"));
1366                 return nt_status;
1367         }
1368
1369         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (fallback_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
1370                 /* do nothing */ ;
1371
1372         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1373
1374
1375         /* did we locate the username in smbpasswd  */
1376         if (smb_pw == NULL)
1377                 return nt_status;
1378         
1379         DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1380                 
1381         if (!sam_acct) {
1382                 DEBUG(10,("getsampwrid: (smbpasswd) SAM_ACCOUNT is NULL\n"));
1383 #if 0
1384                 smb_panic("NULL pointer passed to pdb_getsampwrid\n");
1385 #endif
1386                 return nt_status;
1387         }
1388
1389         /* now build the SAM_ACCOUNT */
1390         if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
1391                 return nt_status;
1392
1393         /* build_sam_account might change the SID on us, if the name was for the guest account */
1394         if (NT_STATUS_IS_OK(nt_status) && !sid_equal(pdb_get_user_sid(sam_acct), sid)) {
1395                 fstring sid_string1, sid_string2;
1396                 DEBUG(1, ("looking for user with sid %s instead returned %s for account %s!?!\n",
1397                           sid_to_string(sid_string1, sid), sid_to_string(sid_string2, pdb_get_user_sid(sam_acct)), pdb_get_username(sam_acct)));
1398                 return NT_STATUS_NO_SUCH_USER;
1399         }
1400
1401         /* success */
1402         return NT_STATUS_OK;
1403 }
1404
1405 static NTSTATUS smbpasswd_add_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1406 {
1407         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1408         struct smb_passwd smb_pw;
1409         
1410         /* convert the SAM_ACCOUNT */
1411         if (!build_smb_pass(&smb_pw, sampass)) {
1412                 return NT_STATUS_UNSUCCESSFUL;
1413         }
1414         
1415         /* add the entry */
1416         if(!add_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1417                 return NT_STATUS_UNSUCCESSFUL;
1418         }
1419         
1420         return NT_STATUS_OK;
1421 }
1422
1423 static NTSTATUS smbpasswd_update_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1424 {
1425         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1426         struct smb_passwd smb_pw;
1427         
1428         /* convert the SAM_ACCOUNT */
1429         if (!build_smb_pass(&smb_pw, sampass)) {
1430                 DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
1431                 return NT_STATUS_UNSUCCESSFUL;
1432         }
1433         
1434         /* update the entry */
1435         if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1436                 DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
1437                 return NT_STATUS_UNSUCCESSFUL;
1438         }
1439         
1440         return NT_STATUS_OK;
1441 }
1442
1443 static NTSTATUS smbpasswd_delete_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1444 {
1445         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1446
1447         const char *username = pdb_get_username(sampass);
1448
1449         if (del_smbfilepwd_entry(smbpasswd_state, username))
1450                 return NT_STATUS_OK;
1451
1452         return NT_STATUS_UNSUCCESSFUL;
1453 }
1454
1455 static void free_private_data(void **vp) 
1456 {
1457         struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
1458         
1459         endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
1460         
1461         *privates = NULL;
1462         /* No need to free any further, as it is talloc()ed */
1463 }
1464
1465 static NTSTATUS pdb_init_smbpasswd(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
1466 {
1467         NTSTATUS nt_status;
1468         struct smbpasswd_privates *privates;
1469
1470         if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
1471                 return nt_status;
1472         }
1473
1474         (*pdb_method)->name = "smbpasswd";
1475
1476         (*pdb_method)->setsampwent = smbpasswd_setsampwent;
1477         (*pdb_method)->endsampwent = smbpasswd_endsampwent;
1478         (*pdb_method)->getsampwent = smbpasswd_getsampwent;
1479         (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
1480         (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
1481         (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
1482         (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
1483         (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
1484
1485         /* Setup private data and free function */
1486
1487         privates = talloc_zero(pdb_context->mem_ctx, sizeof(struct smbpasswd_privates));
1488
1489         if (!privates) {
1490                 DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
1491                 return NT_STATUS_NO_MEMORY;
1492         }
1493
1494         /* Store some config details */
1495
1496         if (location) {
1497                 privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, location);
1498         } else {
1499                 privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, lp_smb_passwd_file());
1500         }
1501         
1502         if (!privates->smbpasswd_file) {
1503                 DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
1504                 return NT_STATUS_NO_MEMORY;
1505         }
1506
1507         (*pdb_method)->private_data = privates;
1508
1509         (*pdb_method)->free_private_data = free_private_data;
1510
1511         return NT_STATUS_OK;
1512 }
1513
1514 NTSTATUS pdb_smbpasswd_init(void) 
1515 {
1516         return smb_register_passdb(PASSDB_INTERFACE_VERSION, "smbpasswd", pdb_init_smbpasswd);
1517 }