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