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