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