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