RIP BOOL. Convert BOOL -> bool. I found a few interesting
[abartlet/samba.git/.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         pstring  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                 /*
441                  * As 256 is shorter than a pstring we don't need to check
442                  * length here - if this ever changes....
443                  */
444                 SMB_ASSERT(sizeof(pstring) > sizeof(linebuf));
445
446                 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
447                 user_name[PTR_DIFF(p, linebuf)] = '\0';
448
449                 /* Get smb uid. */
450
451                 p++; /* Go past ':' */
452
453                 if(*p == '-') {
454                         DEBUG(0, ("getsmbfilepwent: user name %s has a negative uid.\n", user_name));
455                         continue;
456                 }
457
458                 if (!isdigit(*p)) {
459                         DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (uid not number)\n",
460                                 user_name));
461                         continue;
462                 }
463
464                 uidval = atoi((char *) p);
465
466                 while (*p && isdigit(*p)) {
467                         p++;
468                 }
469
470                 if (*p != ':') {
471                         DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (no : after uid)\n",
472                                 user_name));
473                         continue;
474                 }
475
476                 pw_buf->smb_name = user_name;
477                 pw_buf->smb_userid = uidval;
478
479                 /*
480                  * Now get the password value - this should be 32 hex digits
481                  * which are the ascii representations of a 16 byte string.
482                  * Get two at a time and put them into the password.
483                  */
484
485                 /* Skip the ':' */
486                 p++;
487
488                 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
489                         DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (passwd too short)\n",
490                                 user_name ));
491                         continue;
492                 }
493
494                 if (p[32] != ':') {
495                         DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (no terminating :)\n",
496                                 user_name));
497                         continue;
498                 }
499
500                 if (strnequal((char *) p, "NO PASSWORD", 11)) {
501                         pw_buf->smb_passwd = NULL;
502                         pw_buf->acct_ctrl |= ACB_PWNOTREQ;
503                 } else {
504                         if (*p == '*' || *p == 'X') {
505                                 /* NULL LM password */
506                                 pw_buf->smb_passwd = NULL;
507                                 DEBUG(10, ("getsmbfilepwent: LM password for user %s invalidated\n", user_name));
508                         } else if (pdb_gethexpwd((char *)p, smbpwd)) {
509                                 pw_buf->smb_passwd = smbpwd;
510                         } else {
511                                 pw_buf->smb_passwd = NULL;
512                                 DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry for user %s \
513 (non hex chars)\n", user_name));
514                         }
515                 }
516
517                 /* 
518                  * Now check if the NT compatible password is
519                  * available.
520                  */
521                 pw_buf->smb_nt_passwd = NULL;
522                 p += 33; /* Move to the first character of the line after the lanman password. */
523                 if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
524                         if (*p != '*' && *p != 'X') {
525                                 if(pdb_gethexpwd((char *)p,smbntpwd)) {
526                                         pw_buf->smb_nt_passwd = smbntpwd;
527                                 }
528                         }
529                         p += 33; /* Move to the first character of the line after the NT password. */
530                 }
531
532                 DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
533                         user_name, uidval));
534
535                 if (*p == '[') {
536                         unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']');
537                         pw_buf->acct_ctrl = pdb_decode_acct_ctrl((char*)p);
538
539                         /* Must have some account type set. */
540                         if(pw_buf->acct_ctrl == 0) {
541                                 pw_buf->acct_ctrl = ACB_NORMAL;
542                         }
543
544                         /* Now try and get the last change time. */
545                         if(end_p) {
546                                 p = end_p + 1;
547                         }
548                         if(*p == ':') {
549                                 p++;
550                                 if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
551                                         int i;
552                                         p += 4;
553                                         for(i = 0; i < 8; i++) {
554                                                 if(p[i] == '\0' || !isxdigit(p[i])) {
555                                                         break;
556                                                 }
557                                         }
558                                         if(i == 8) {
559                                                 /*
560                                                  * p points at 8 characters of hex digits - 
561                                                  * read into a time_t as the seconds since
562                                                  * 1970 that the password was last changed.
563                                                  */
564                                                 pw_buf->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
565                                         }
566                                 }
567                         }
568                 } else {
569                         /* 'Old' style file. Fake up based on user name. */
570                         /*
571                          * Currently trust accounts are kept in the same
572                          * password file as 'normal accounts'. If this changes
573                          * we will have to fix this code. JRA.
574                          */
575                         if(pw_buf->smb_name[strlen(pw_buf->smb_name) - 1] == '$') {
576                                 pw_buf->acct_ctrl &= ~ACB_NORMAL;
577                                 pw_buf->acct_ctrl |= ACB_WSTRUST;
578                         }
579                 }
580
581                 return pw_buf;
582         }
583
584         DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
585         return NULL;
586 }
587
588 /************************************************************************
589  Create a new smbpasswd entry - malloced space returned.
590 *************************************************************************/
591
592 static char *format_new_smbpasswd_entry(const struct smb_passwd *newpwd)
593 {
594         int new_entry_length;
595         char *new_entry;
596         char *p;
597
598         new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + 
599                                 NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
600
601         if((new_entry = (char *)SMB_MALLOC( new_entry_length )) == NULL) {
602                 DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n",
603                         newpwd->smb_name ));
604                 return NULL;
605         }
606
607         slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
608
609         p = new_entry+strlen(new_entry);
610         pdb_sethexpwd(p, newpwd->smb_passwd, newpwd->acct_ctrl);
611         p+=strlen(p);
612         *p = ':';
613         p++;
614
615         pdb_sethexpwd(p, newpwd->smb_nt_passwd, newpwd->acct_ctrl);
616         p+=strlen(p);
617         *p = ':';
618         p++;
619
620         /* Add the account encoding and the last change time. */
621         slprintf((char *)p, new_entry_length - 1 - (p - new_entry),  "%s:LCT-%08X:\n",
622                 pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
623                 (uint32)newpwd->pass_last_set_time);
624
625         return new_entry;
626 }
627
628 /************************************************************************
629  Routine to add an entry to the smbpasswd file.
630 *************************************************************************/
631
632 static NTSTATUS add_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state,
633                                      struct smb_passwd *newpwd)
634 {
635         const char *pfile = smbpasswd_state->smbpasswd_file;
636         struct smb_passwd *pwd = NULL;
637         FILE *fp = NULL;
638         int wr_len;
639         int fd;
640         size_t new_entry_length;
641         char *new_entry;
642         SMB_OFF_T offpos;
643  
644         /* Open the smbpassword file - for update. */
645         fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth);
646
647         if (fp == NULL && errno == ENOENT) {
648                 /* Try again - create. */
649                 fp = startsmbfilepwent(pfile, PWF_CREATE, &smbpasswd_state->pw_file_lock_depth);
650         }
651
652         if (fp == NULL) {
653                 DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
654                 return map_nt_error_from_unix(errno);
655         }
656
657         /*
658          * Scan the file, a line at a time and check if the name matches.
659          */
660
661         while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
662                 if (strequal(newpwd->smb_name, pwd->smb_name)) {
663                         DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
664                         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
665                         return NT_STATUS_USER_EXISTS;
666                 }
667         }
668
669         /* Ok - entry doesn't exist. We can add it */
670
671         /* Create a new smb passwd entry and set it to the given password. */
672         /* 
673          * The add user write needs to be atomic - so get the fd from 
674          * the fp and do a raw write() call.
675          */
676         fd = fileno(fp);
677
678         if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) {
679                 NTSTATUS result = map_nt_error_from_unix(errno);
680                 DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
681 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
682                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
683                 return result;
684         }
685
686         if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) {
687                 DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
688 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
689                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
690                 return NT_STATUS_NO_MEMORY;
691         }
692
693         new_entry_length = strlen(new_entry);
694
695 #ifdef DEBUG_PASSWORD
696         DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|", 
697                         fd, (int)new_entry_length, new_entry));
698 #endif
699
700         if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) {
701                 NTSTATUS result = map_nt_error_from_unix(errno);
702                 DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
703 Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
704
705                 /* Remove the entry we just wrote. */
706                 if(sys_ftruncate(fd, offpos) == -1) {
707                         DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
708 Error was %s. Password file may be corrupt ! Please examine by hand !\n", 
709                                 newpwd->smb_name, strerror(errno)));
710                 }
711
712                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
713                 free(new_entry);
714                 return result;
715         }
716
717         free(new_entry);
718         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
719         return NT_STATUS_OK;
720 }
721
722 /************************************************************************
723  Routine to search the smbpasswd file for an entry matching the username.
724  and then modify its password entry. We can't use the startsmbpwent()/
725  getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
726  in the actual file to decide how much room we have to write data.
727  override = False, normal
728  override = True, override XXXXXXXX'd out password or NO PASS
729 ************************************************************************/
730
731 static bool mod_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const struct smb_passwd* pwd)
732 {
733         /* Static buffers we will return. */
734         pstring user_name;
735
736         char *status;
737         char linebuf[256];
738         char readbuf[1024];
739         int c;
740         fstring ascii_p16;
741         fstring encode_bits;
742         unsigned char *p = NULL;
743         size_t linebuf_len = 0;
744         FILE *fp;
745         int lockfd;
746         const char *pfile = smbpasswd_state->smbpasswd_file;
747         bool found_entry = False;
748         bool got_pass_last_set_time = False;
749
750         SMB_OFF_T pwd_seekpos = 0;
751
752         int i;
753         int wr_len;
754         int fd;
755
756         if (!*pfile) {
757                 DEBUG(0, ("No SMB password file set\n"));
758                 return False;
759         }
760         DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
761
762         fp = sys_fopen(pfile, "r+");
763
764         if (fp == NULL) {
765                 DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
766                 return False;
767         }
768         /* Set a buffer to do more efficient reads */
769         setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
770
771         lockfd = fileno(fp);
772
773         if (!pw_file_lock(lockfd, F_WRLCK, 5, &smbpasswd_state->pw_file_lock_depth)) {
774                 DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
775                 fclose(fp);
776                 return False;
777         }
778
779         /* Make sure it is only rw by the owner */
780         chmod(pfile, 0600);
781
782         /* We have a write lock on the file. */
783         /*
784          * Scan the file, a line at a time and check if the name matches.
785          */
786         status = linebuf;
787         while (status && !feof(fp)) {
788                 pwd_seekpos = sys_ftell(fp);
789
790                 linebuf[0] = '\0';
791
792                 status = fgets(linebuf, sizeof(linebuf), fp);
793                 if (status == NULL && ferror(fp)) {
794                         pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
795                         fclose(fp);
796                         return False;
797                 }
798
799                 /*
800                  * Check if the string is terminated with a newline - if not
801                  * then we must keep reading and discard until we get one.
802                  */
803                 linebuf_len = strlen(linebuf);
804                 if (linebuf[linebuf_len - 1] != '\n') {
805                         c = '\0';
806                         while (!ferror(fp) && !feof(fp)) {
807                                 c = fgetc(fp);
808                                 if (c == '\n') {
809                                         break;
810                                 }
811                         }
812                 } else {
813                         linebuf[linebuf_len - 1] = '\0';
814                 }
815
816 #ifdef DEBUG_PASSWORD
817                 DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
818 #endif
819
820                 if ((linebuf[0] == 0) && feof(fp)) {
821                         DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
822                         break;
823                 }
824
825                 /*
826                  * The line we have should be of the form :-
827                  * 
828                  * username:uid:[32hex bytes]:....other flags presently
829                  * ignored....
830                  * 
831                  * or,
832                  *
833                  * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
834                  *
835                  * if Windows NT compatible passwords are also present.
836                  */
837
838                 if (linebuf[0] == '#' || linebuf[0] == '\0') {
839                         DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
840                         continue;
841                 }
842
843                 p = (unsigned char *) strchr_m(linebuf, ':');
844
845                 if (p == NULL) {
846                         DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
847                         continue;
848                 }
849
850                 /*
851                  * As 256 is shorter than a pstring we don't need to check
852                  * length here - if this ever changes....
853                  */
854
855                 SMB_ASSERT(sizeof(user_name) > sizeof(linebuf));
856
857                 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
858                 user_name[PTR_DIFF(p, linebuf)] = '\0';
859                 if (strequal(user_name, pwd->smb_name)) {
860                         found_entry = True;
861                         break;
862                 }
863         }
864
865         if (!found_entry) {
866                 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
867                 fclose(fp);
868
869                 DEBUG(2, ("Cannot update entry for user %s, as they don't exist in the smbpasswd file!\n",
870                         pwd->smb_name));
871                 return False;
872         }
873
874         DEBUG(6, ("mod_smbfilepwd_entry: entry exists for user %s\n", pwd->smb_name));
875
876         /* User name matches - get uid and password */
877         p++; /* Go past ':' */
878
879         if (!isdigit(*p)) {
880                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (uid not number)\n",
881                         pwd->smb_name));
882                 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
883                 fclose(fp);
884                 return False;
885         }
886
887         while (*p && isdigit(*p)) {
888                 p++;
889         }
890         if (*p != ':') {
891                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no : after uid)\n",
892                         pwd->smb_name));
893                 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
894                 fclose(fp);
895                 return False;
896         }
897
898         /*
899          * Now get the password value - this should be 32 hex digits
900          * which are the ascii representations of a 16 byte string.
901          * Get two at a time and put them into the password.
902          */
903         p++;
904
905         /* Record exact password position */
906         pwd_seekpos += PTR_DIFF(p, linebuf);
907
908         if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
909                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
910                         pwd->smb_name));
911                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
912                 fclose(fp);
913                 return (False);
914         }
915
916         if (p[32] != ':') {
917                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
918                         pwd->smb_name));
919                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
920                 fclose(fp);
921                 return False;
922         }
923
924         /* Now check if the NT compatible password is available. */
925         p += 33; /* Move to the first character of the line after the lanman password. */
926         if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
927                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
928                         pwd->smb_name));
929                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
930                 fclose(fp);
931                 return (False);
932         }
933
934         if (p[32] != ':') {
935                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
936                         pwd->smb_name));
937                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
938                 fclose(fp);
939                 return False;
940         }
941
942         /* 
943          * Now check if the account info and the password last
944          * change time is available.
945          */
946         p += 33; /* Move to the first character of the line after the NT password. */
947
948         if (*p == '[') {
949                 i = 0;
950                 encode_bits[i++] = *p++;
951                 while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']')) {
952                         encode_bits[i++] = *p++;
953                 }
954
955                 encode_bits[i++] = ']';
956                 encode_bits[i++] = '\0';
957
958                 if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
959                         /*
960                          * We are using a new format, space padded
961                          * acct ctrl field. Encode the given acct ctrl
962                          * bits into it.
963                          */
964                         fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
965                 } else {
966                         DEBUG(0,("mod_smbfilepwd_entry:  Using old smbpasswd format for user %s. \
967 This is no longer supported.!\n", pwd->smb_name));
968                         DEBUG(0,("mod_smbfilepwd_entry:  No changes made, failing.!\n"));
969                         pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
970                         fclose(fp);
971                         return False;
972                 }
973
974                 /* Go past the ']' */
975                 if(linebuf_len > PTR_DIFF(p, linebuf)) {
976                         p++;
977                 }
978
979                 if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
980                         p++;
981
982                         /* We should be pointing at the LCT entry. */
983                         if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
984                                 p += 4;
985                                 for(i = 0; i < 8; i++) {
986                                         if(p[i] == '\0' || !isxdigit(p[i])) {
987                                                 break;
988                                         }
989                                 }
990                                 if(i == 8) {
991                                         /*
992                                          * p points at 8 characters of hex digits -
993                                          * read into a time_t as the seconds since
994                                          * 1970 that the password was last changed.
995                                          */
996                                         got_pass_last_set_time = True;
997                                 } /* i == 8 */
998                         } /* *p && StrnCaseCmp() */
999                 } /* p == ':' */
1000         } /* p == '[' */
1001
1002         /* Entry is correctly formed. */
1003
1004         /* Create the 32 byte representation of the new p16 */
1005         pdb_sethexpwd(ascii_p16, pwd->smb_passwd, pwd->acct_ctrl);
1006
1007         /* Add on the NT md4 hash */
1008         ascii_p16[32] = ':';
1009         wr_len = 66;
1010         pdb_sethexpwd(ascii_p16+33, pwd->smb_nt_passwd, pwd->acct_ctrl);
1011         ascii_p16[65] = ':';
1012         ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
1013
1014         /* Add on the account info bits and the time of last password change. */
1015         if(got_pass_last_set_time) {
1016                 slprintf(&ascii_p16[strlen(ascii_p16)], 
1017                         sizeof(ascii_p16)-(strlen(ascii_p16)+1),
1018                         "%s:LCT-%08X:", 
1019                         encode_bits, (uint32)pwd->pass_last_set_time );
1020                 wr_len = strlen(ascii_p16);
1021         }
1022
1023 #ifdef DEBUG_PASSWORD
1024         DEBUG(100,("mod_smbfilepwd_entry: "));
1025         dump_data(100, (uint8 *)ascii_p16, wr_len);
1026 #endif
1027
1028         if(wr_len > sizeof(linebuf)) {
1029                 DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
1030                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1031                 fclose(fp);
1032                 return (False);
1033         }
1034
1035         /*
1036          * Do an atomic write into the file at the position defined by
1037          * seekpos.
1038          */
1039
1040         /* The mod user write needs to be atomic - so get the fd from 
1041                 the fp and do a raw write() call.
1042          */
1043
1044         fd = fileno(fp);
1045
1046         if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
1047                 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1048                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1049                 fclose(fp);
1050                 return False;
1051         }
1052
1053         /* Sanity check - ensure the areas we are writing are framed by ':' */
1054         if (read(fd, linebuf, wr_len+1) != wr_len+1) {
1055                 DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
1056                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1057                 fclose(fp);
1058                 return False;
1059         }
1060
1061         if ((linebuf[0] != ':') || (linebuf[wr_len] != ':'))    {
1062                 DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
1063                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1064                 fclose(fp);
1065                 return False;
1066         }
1067  
1068         if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
1069                 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1070                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1071                 fclose(fp);
1072                 return False;
1073         }
1074
1075         if (write(fd, ascii_p16, wr_len) != wr_len) {
1076                 DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
1077                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1078                 fclose(fp);
1079                 return False;
1080         }
1081
1082         pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1083         fclose(fp);
1084         return True;
1085 }
1086
1087 /************************************************************************
1088  Routine to delete an entry in the smbpasswd file by name.
1089 *************************************************************************/
1090
1091 static bool del_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const char *name)
1092 {
1093         const char *pfile = smbpasswd_state->smbpasswd_file;
1094         pstring pfile2;
1095         struct smb_passwd *pwd = NULL;
1096         FILE *fp = NULL;
1097         FILE *fp_write = NULL;
1098         int pfile2_lockdepth = 0;
1099
1100         slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)sys_getpid() );
1101
1102         /*
1103          * Open the smbpassword file - for update. It needs to be update
1104          * as we need any other processes to wait until we have replaced
1105          * it.
1106          */
1107
1108         if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth)) == NULL) {
1109                 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1110                 return False;
1111         }
1112
1113         /*
1114          * Create the replacement password file.
1115          */
1116         if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
1117                 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1118                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1119                 return False;
1120         }
1121
1122         /*
1123          * Scan the file, a line at a time and check if the name matches.
1124          */
1125
1126         while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
1127                 char *new_entry;
1128                 size_t new_entry_length;
1129
1130                 if (strequal(name, pwd->smb_name)) {
1131                         DEBUG(10, ("del_smbfilepwd_entry: found entry with "
1132                                    "name %s - deleting it.\n", name));
1133                         continue;
1134                 }
1135
1136                 /*
1137                  * We need to copy the entry out into the second file.
1138                  */
1139
1140                 if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) {
1141                         DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
1142 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1143                         unlink(pfile2);
1144                         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1145                         endsmbfilepwent(fp_write, &pfile2_lockdepth);
1146                         return False;
1147                 }
1148
1149                 new_entry_length = strlen(new_entry);
1150
1151                 if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) {
1152                         DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
1153 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1154                         unlink(pfile2);
1155                         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1156                         endsmbfilepwent(fp_write, &pfile2_lockdepth);
1157                         free(new_entry);
1158                         return False;
1159                 }
1160
1161                 free(new_entry);
1162         }
1163
1164         /*
1165          * Ensure pfile2 is flushed before rename.
1166          */
1167
1168         if(fflush(fp_write) != 0) {
1169                 DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
1170                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1171                 endsmbfilepwent(fp_write,&pfile2_lockdepth);
1172                 return False;
1173         }
1174
1175         /*
1176          * Do an atomic rename - then release the locks.
1177          */
1178
1179         if(rename(pfile2,pfile) != 0) {
1180                 unlink(pfile2);
1181         }
1182   
1183         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1184         endsmbfilepwent(fp_write,&pfile2_lockdepth);
1185         return True;
1186 }
1187
1188 /*********************************************************************
1189  Create a smb_passwd struct from a struct samu.
1190  We will not allocate any new memory.  The smb_passwd struct
1191  should only stay around as long as the struct samu does.
1192  ********************************************************************/
1193
1194 static bool build_smb_pass (struct smb_passwd *smb_pw, const struct samu *sampass)
1195 {
1196         uint32 rid;
1197
1198         if (sampass == NULL) 
1199                 return False;
1200         ZERO_STRUCTP(smb_pw);
1201
1202         if (!IS_SAM_DEFAULT(sampass, PDB_USERSID)) {
1203                 rid = pdb_get_user_rid(sampass);
1204                 
1205                 /* If the user specified a RID, make sure its able to be both stored and retreived */
1206                 if (rid == DOMAIN_USER_RID_GUEST) {
1207                         struct passwd *passwd = getpwnam_alloc(NULL, lp_guestaccount());
1208                         if (!passwd) {
1209                                 DEBUG(0, ("Could not find guest account via getpwnam()! (%s)\n", lp_guestaccount()));
1210                                 return False;
1211                         }
1212                         smb_pw->smb_userid=passwd->pw_uid;
1213                         TALLOC_FREE(passwd);
1214                 } else if (algorithmic_pdb_rid_is_user(rid)) {
1215                         smb_pw->smb_userid=algorithmic_pdb_user_rid_to_uid(rid);
1216                 } else {
1217                         DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
1218                         return False;
1219                 }
1220         }
1221
1222         smb_pw->smb_name=(const char*)pdb_get_username(sampass);
1223
1224         smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
1225         smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
1226
1227         smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
1228         smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
1229
1230         return True;
1231 }       
1232
1233 /*********************************************************************
1234  Create a struct samu from a smb_passwd struct
1235  ********************************************************************/
1236
1237 static bool build_sam_account(struct smbpasswd_privates *smbpasswd_state, 
1238                               struct samu *sam_pass, const struct smb_passwd *pw_buf)
1239 {
1240         struct passwd *pwfile;
1241         
1242         if ( !sam_pass ) {
1243                 DEBUG(5,("build_sam_account: struct samu is NULL\n"));
1244                 return False;
1245         }
1246
1247         /* verify the user account exists */
1248
1249         if ( !(pwfile = Get_Pwnam_alloc(NULL, pw_buf->smb_name )) ) {
1250                 DEBUG(0,("build_sam_account: smbpasswd database is corrupt!  username %s with uid "
1251                 "%u is not in unix passwd database!\n", pw_buf->smb_name, pw_buf->smb_userid));
1252                         return False;
1253         }
1254         
1255         if ( !NT_STATUS_IS_OK( samu_set_unix(sam_pass, pwfile )) )
1256                 return False;
1257                 
1258         TALLOC_FREE(pwfile);
1259
1260         /* set remaining fields */
1261                 
1262         pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd, PDB_SET);
1263         pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd, PDB_SET);                  
1264         pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl, PDB_SET);
1265         pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1266         pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1267         
1268         return True;
1269 }
1270
1271 /*****************************************************************
1272  Functions to be implemented by the new passdb API 
1273  ****************************************************************/
1274
1275 static NTSTATUS smbpasswd_setsampwent (struct pdb_methods *my_methods, bool update, uint32 acb_mask)
1276 {
1277         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1278         
1279         smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
1280                                                        update ? PWF_UPDATE : PWF_READ, 
1281                                                        &(smbpasswd_state->pw_file_lock_depth));
1282                                    
1283         /* did we fail?  Should we try to create it? */
1284         if (!smbpasswd_state->pw_file && update && errno == ENOENT) {
1285                 FILE *fp;
1286                 /* slprintf(msg_str,msg_str_len-1,
1287                    "smbpasswd file did not exist - attempting to create it.\n"); */
1288                 DEBUG(0,("smbpasswd file did not exist - attempting to create it.\n"));
1289                 fp = sys_fopen(smbpasswd_state->smbpasswd_file, "w");
1290                 if (fp) {
1291                         fprintf(fp, "# Samba SMB password file\n");
1292                         fclose(fp);
1293                 }
1294                 
1295                 smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
1296                                                              update ? PWF_UPDATE : PWF_READ, 
1297                                                              &(smbpasswd_state->pw_file_lock_depth));
1298         }
1299         
1300         if (smbpasswd_state->pw_file != NULL)
1301                 return NT_STATUS_OK;
1302         else
1303                 return NT_STATUS_UNSUCCESSFUL;  
1304 }
1305
1306 static void smbpasswd_endsampwent (struct pdb_methods *my_methods)
1307 {
1308         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1309         endsmbfilepwent(smbpasswd_state->pw_file, &(smbpasswd_state->pw_file_lock_depth));
1310 }
1311  
1312 /*****************************************************************
1313  ****************************************************************/
1314
1315 static NTSTATUS smbpasswd_getsampwent(struct pdb_methods *my_methods, struct samu *user)
1316 {
1317         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1318         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1319         struct smb_passwd *pw_buf=NULL;
1320         bool done = False;
1321
1322         DEBUG(5,("pdb_getsampwent\n"));
1323
1324         if ( !user ) {
1325                 DEBUG(5,("pdb_getsampwent (smbpasswd): user is NULL\n"));
1326                 return nt_status;
1327         }
1328
1329         while (!done) {
1330                 /* do we have an entry? */
1331                 pw_buf = getsmbfilepwent(smbpasswd_state, smbpasswd_state->pw_file);
1332                 if (pw_buf == NULL) 
1333                         return nt_status;
1334
1335                 /* build the struct samu entry from the smb_passwd struct. 
1336                    We loop in case the user in the pdb does not exist in 
1337                    the local system password file */
1338                 if (build_sam_account(smbpasswd_state, user, pw_buf))
1339                         done = True;
1340         }
1341
1342         DEBUG(5,("getsampwent (smbpasswd): done\n"));
1343
1344         /* success */
1345         return NT_STATUS_OK;
1346 }
1347
1348 /****************************************************************
1349  Search smbpasswd file by iterating over the entries.  Do not
1350  call getpwnam() for unix account information until we have found
1351  the correct entry
1352  ***************************************************************/
1353
1354 static NTSTATUS smbpasswd_getsampwnam(struct pdb_methods *my_methods, 
1355                                   struct samu *sam_acct, const char *username)
1356 {
1357         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1358         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1359         struct smb_passwd *smb_pw;
1360         FILE *fp = NULL;
1361
1362         DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
1363
1364         /* startsmbfilepwent() is used here as we don't want to lookup
1365            the UNIX account in the local system password file until
1366            we have a match.  */
1367         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1368
1369         if (fp == NULL) {
1370                 DEBUG(0, ("Unable to open passdb database.\n"));
1371                 return nt_status;
1372         }
1373
1374         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
1375                 /* do nothing....another loop */ ;
1376         
1377         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1378
1379
1380         /* did we locate the username in smbpasswd  */
1381         if (smb_pw == NULL)
1382                 return nt_status;
1383         
1384         DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1385
1386         if (!sam_acct) {
1387                 DEBUG(10,("getsampwnam (smbpasswd): struct samu is NULL\n"));
1388                 return nt_status;
1389         }
1390                 
1391         /* now build the struct samu */
1392         if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
1393                 return nt_status;
1394
1395         /* success */
1396         return NT_STATUS_OK;
1397 }
1398
1399 static NTSTATUS smbpasswd_getsampwsid(struct pdb_methods *my_methods, struct samu *sam_acct, const DOM_SID *sid)
1400 {
1401         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1402         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1403         struct smb_passwd *smb_pw;
1404         FILE *fp = NULL;
1405         fstring sid_str;
1406         uint32 rid;
1407         
1408         DEBUG(10, ("smbpasswd_getsampwrid: search by sid: %s\n", sid_to_string(sid_str, sid)));
1409
1410         if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
1411                 return NT_STATUS_UNSUCCESSFUL;
1412
1413         /* More special case 'guest account' hacks... */
1414         if (rid == DOMAIN_USER_RID_GUEST) {
1415                 const char *guest_account = lp_guestaccount();
1416                 if (!(guest_account && *guest_account)) {
1417                         DEBUG(1, ("Guest account not specfied!\n"));
1418                         return nt_status;
1419                 }
1420                 return smbpasswd_getsampwnam(my_methods, sam_acct, guest_account);
1421         }
1422
1423         /* Open the sam password file - not for update. */
1424         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1425
1426         if (fp == NULL) {
1427                 DEBUG(0, ("Unable to open passdb database.\n"));
1428                 return nt_status;
1429         }
1430
1431         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (algorithmic_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
1432                 /* do nothing */ ;
1433
1434         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1435
1436
1437         /* did we locate the username in smbpasswd  */
1438         if (smb_pw == NULL)
1439                 return nt_status;
1440         
1441         DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1442                 
1443         if (!sam_acct) {
1444                 DEBUG(10,("getsampwrid: (smbpasswd) struct samu is NULL\n"));
1445                 return nt_status;
1446         }
1447
1448         /* now build the struct samu */
1449         if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
1450                 return nt_status;
1451
1452         /* build_sam_account might change the SID on us, if the name was for the guest account */
1453         if (NT_STATUS_IS_OK(nt_status) && !sid_equal(pdb_get_user_sid(sam_acct), sid)) {
1454                 fstring sid_string1, sid_string2;
1455                 DEBUG(1, ("looking for user with sid %s instead returned %s for account %s!?!\n",
1456                           sid_to_string(sid_string1, sid), sid_to_string(sid_string2, pdb_get_user_sid(sam_acct)), pdb_get_username(sam_acct)));
1457                 return NT_STATUS_NO_SUCH_USER;
1458         }
1459
1460         /* success */
1461         return NT_STATUS_OK;
1462 }
1463
1464 static NTSTATUS smbpasswd_add_sam_account(struct pdb_methods *my_methods, struct samu *sampass)
1465 {
1466         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1467         struct smb_passwd smb_pw;
1468         
1469         /* convert the struct samu */
1470         if (!build_smb_pass(&smb_pw, sampass)) {
1471                 return NT_STATUS_UNSUCCESSFUL;
1472         }
1473         
1474         /* add the entry */
1475         return add_smbfilepwd_entry(smbpasswd_state, &smb_pw);
1476 }
1477
1478 static NTSTATUS smbpasswd_update_sam_account(struct pdb_methods *my_methods, struct samu *sampass)
1479 {
1480         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1481         struct smb_passwd smb_pw;
1482         
1483         /* convert the struct samu */
1484         if (!build_smb_pass(&smb_pw, sampass)) {
1485                 DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
1486                 return NT_STATUS_UNSUCCESSFUL;
1487         }
1488         
1489         /* update the entry */
1490         if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1491                 DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
1492                 return NT_STATUS_UNSUCCESSFUL;
1493         }
1494         
1495         return NT_STATUS_OK;
1496 }
1497
1498 static NTSTATUS smbpasswd_delete_sam_account (struct pdb_methods *my_methods, struct samu *sampass)
1499 {
1500         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1501
1502         const char *username = pdb_get_username(sampass);
1503
1504         if (del_smbfilepwd_entry(smbpasswd_state, username))
1505                 return NT_STATUS_OK;
1506
1507         return NT_STATUS_UNSUCCESSFUL;
1508 }
1509
1510 static NTSTATUS smbpasswd_rename_sam_account (struct pdb_methods *my_methods, 
1511                                               struct samu *old_acct,
1512                                               const char *newname)
1513 {
1514         pstring rename_script;
1515         struct samu *new_acct = NULL;
1516         bool interim_account = False;
1517         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
1518
1519         if (!*(lp_renameuser_script()))
1520                 goto done;
1521
1522         if ( !(new_acct = samu_new( NULL )) ) {
1523                 return NT_STATUS_NO_MEMORY;
1524         }
1525         
1526         if ( !pdb_copy_sam_account( new_acct, old_acct ) 
1527                 || !pdb_set_username(new_acct, newname, PDB_CHANGED)) 
1528         {
1529                 goto done;
1530         }
1531         
1532         ret = smbpasswd_add_sam_account(my_methods, new_acct);
1533         if (!NT_STATUS_IS_OK(ret))
1534                 goto done;
1535
1536         interim_account = True;
1537
1538         /* rename the posix user */
1539         pstrcpy(rename_script, lp_renameuser_script());
1540
1541         if (*rename_script) {
1542                 int rename_ret;
1543
1544                 string_sub2(rename_script, "%unew", newname, sizeof(pstring), 
1545                             True, False, True);
1546                 string_sub2(rename_script, "%uold", pdb_get_username(old_acct), 
1547                             sizeof(pstring), True, False, True);
1548
1549                 rename_ret = smbrun(rename_script, NULL);
1550
1551                 DEBUG(rename_ret ? 0 : 3,("Running the command `%s' gave %d\n", rename_script, rename_ret));
1552
1553                 if (rename_ret == 0) {
1554                         smb_nscd_flush_user_cache();
1555                 }
1556
1557                 if (rename_ret) 
1558                         goto done; 
1559         } else {
1560                 goto done;
1561         }
1562
1563         smbpasswd_delete_sam_account(my_methods, old_acct);
1564         interim_account = False;
1565
1566 done:   
1567         /* cleanup */
1568         if (interim_account)
1569                 smbpasswd_delete_sam_account(my_methods, new_acct);
1570
1571         if (new_acct)
1572                 TALLOC_FREE(new_acct);
1573         
1574         return (ret);   
1575 }
1576
1577 static bool smbpasswd_rid_algorithm(struct pdb_methods *methods)
1578 {
1579         return True;
1580 }
1581
1582 static void free_private_data(void **vp) 
1583 {
1584         struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
1585         
1586         endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
1587         
1588         *privates = NULL;
1589         /* No need to free any further, as it is talloc()ed */
1590 }
1591
1592 static NTSTATUS pdb_init_smbpasswd( struct pdb_methods **pdb_method, const char *location )
1593 {
1594         NTSTATUS nt_status;
1595         struct smbpasswd_privates *privates;
1596
1597         if ( !NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method )) ) {
1598                 return nt_status;
1599         }
1600
1601         (*pdb_method)->name = "smbpasswd";
1602
1603         (*pdb_method)->setsampwent = smbpasswd_setsampwent;
1604         (*pdb_method)->endsampwent = smbpasswd_endsampwent;
1605         (*pdb_method)->getsampwent = smbpasswd_getsampwent;
1606         (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
1607         (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
1608         (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
1609         (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
1610         (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
1611         (*pdb_method)->rename_sam_account = smbpasswd_rename_sam_account;
1612
1613         (*pdb_method)->rid_algorithm = smbpasswd_rid_algorithm;
1614
1615         /* Setup private data and free function */
1616
1617         if ( !(privates = TALLOC_ZERO_P( *pdb_method, struct smbpasswd_privates )) ) {
1618                 DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
1619                 return NT_STATUS_NO_MEMORY;
1620         }
1621
1622         /* Store some config details */
1623
1624         if (location) {
1625                 privates->smbpasswd_file = talloc_strdup(*pdb_method, location);
1626         } else {
1627                 privates->smbpasswd_file = talloc_strdup(*pdb_method, lp_smb_passwd_file());
1628         }
1629         
1630         if (!privates->smbpasswd_file) {
1631                 DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
1632                 return NT_STATUS_NO_MEMORY;
1633         }
1634
1635         (*pdb_method)->private_data = privates;
1636
1637         (*pdb_method)->free_private_data = free_private_data;
1638
1639         return NT_STATUS_OK;
1640 }
1641
1642 NTSTATUS pdb_smbpasswd_init(void) 
1643 {
1644         return smb_register_passdb(PASSDB_INTERFACE_VERSION, "smbpasswd", pdb_init_smbpasswd);
1645 }