Fix valgrind errors
[nivanova/samba-autobuild/.git] / source3 / passdb / pdb_smbpasswd.c
1 /*
2  * Unix SMB/CIFS implementation. 
3  * SMB parameters and setup
4  * Copyright (C) Andrew Tridgell       1992-1998 
5  * Modified by Jeremy Allison          1995.
6  * Modified by Gerald (Jerry) Carter   2000-2001,2003
7  * Modified by Andrew Bartlett         2002.
8  * 
9  * This program is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free
11  * Software Foundation; either version 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 /****************************************************************
1268  Search smbpasswd file by iterating over the entries.  Do not
1269  call getpwnam() for unix account information until we have found
1270  the correct entry
1271  ***************************************************************/
1272
1273 static NTSTATUS smbpasswd_getsampwnam(struct pdb_methods *my_methods, 
1274                                   struct samu *sam_acct, const char *username)
1275 {
1276         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1277         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1278         struct smb_passwd *smb_pw;
1279         FILE *fp = NULL;
1280
1281         DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
1282
1283         /* startsmbfilepwent() is used here as we don't want to lookup
1284            the UNIX account in the local system password file until
1285            we have a match.  */
1286         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1287
1288         if (fp == NULL) {
1289                 DEBUG(0, ("Unable to open passdb database.\n"));
1290                 return nt_status;
1291         }
1292
1293         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
1294                 /* do nothing....another loop */ ;
1295         
1296         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1297
1298
1299         /* did we locate the username in smbpasswd  */
1300         if (smb_pw == NULL)
1301                 return nt_status;
1302         
1303         DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1304
1305         if (!sam_acct) {
1306                 DEBUG(10,("getsampwnam (smbpasswd): struct samu is NULL\n"));
1307                 return nt_status;
1308         }
1309                 
1310         /* now build the struct samu */
1311         if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
1312                 return nt_status;
1313
1314         /* success */
1315         return NT_STATUS_OK;
1316 }
1317
1318 static NTSTATUS smbpasswd_getsampwsid(struct pdb_methods *my_methods, struct samu *sam_acct, const DOM_SID *sid)
1319 {
1320         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1321         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1322         struct smb_passwd *smb_pw;
1323         FILE *fp = NULL;
1324         uint32 rid;
1325         
1326         DEBUG(10, ("smbpasswd_getsampwrid: search by sid: %s\n",
1327                    sid_string_dbg(sid)));
1328
1329         if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
1330                 return NT_STATUS_UNSUCCESSFUL;
1331
1332         /* More special case 'guest account' hacks... */
1333         if (rid == DOMAIN_USER_RID_GUEST) {
1334                 const char *guest_account = lp_guestaccount();
1335                 if (!(guest_account && *guest_account)) {
1336                         DEBUG(1, ("Guest account not specfied!\n"));
1337                         return nt_status;
1338                 }
1339                 return smbpasswd_getsampwnam(my_methods, sam_acct, guest_account);
1340         }
1341
1342         /* Open the sam password file - not for update. */
1343         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1344
1345         if (fp == NULL) {
1346                 DEBUG(0, ("Unable to open passdb database.\n"));
1347                 return nt_status;
1348         }
1349
1350         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (algorithmic_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
1351                 /* do nothing */ ;
1352
1353         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1354
1355
1356         /* did we locate the username in smbpasswd  */
1357         if (smb_pw == NULL)
1358                 return nt_status;
1359         
1360         DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1361                 
1362         if (!sam_acct) {
1363                 DEBUG(10,("getsampwrid: (smbpasswd) struct samu is NULL\n"));
1364                 return nt_status;
1365         }
1366
1367         /* now build the struct samu */
1368         if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
1369                 return nt_status;
1370
1371         /* build_sam_account might change the SID on us, if the name was for the guest account */
1372         if (NT_STATUS_IS_OK(nt_status) && !sid_equal(pdb_get_user_sid(sam_acct), sid)) {
1373                 DEBUG(1, ("looking for user with sid %s instead returned %s "
1374                           "for account %s!?!\n", sid_string_dbg(sid),
1375                           sid_string_dbg(pdb_get_user_sid(sam_acct)),
1376                           pdb_get_username(sam_acct)));
1377                 return NT_STATUS_NO_SUCH_USER;
1378         }
1379
1380         /* success */
1381         return NT_STATUS_OK;
1382 }
1383
1384 static NTSTATUS smbpasswd_add_sam_account(struct pdb_methods *my_methods, struct samu *sampass)
1385 {
1386         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1387         struct smb_passwd smb_pw;
1388         
1389         /* convert the struct samu */
1390         if (!build_smb_pass(&smb_pw, sampass)) {
1391                 return NT_STATUS_UNSUCCESSFUL;
1392         }
1393         
1394         /* add the entry */
1395         return add_smbfilepwd_entry(smbpasswd_state, &smb_pw);
1396 }
1397
1398 static NTSTATUS smbpasswd_update_sam_account(struct pdb_methods *my_methods, struct samu *sampass)
1399 {
1400         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1401         struct smb_passwd smb_pw;
1402         
1403         /* convert the struct samu */
1404         if (!build_smb_pass(&smb_pw, sampass)) {
1405                 DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
1406                 return NT_STATUS_UNSUCCESSFUL;
1407         }
1408         
1409         /* update the entry */
1410         if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1411                 DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
1412                 return NT_STATUS_UNSUCCESSFUL;
1413         }
1414         
1415         return NT_STATUS_OK;
1416 }
1417
1418 static NTSTATUS smbpasswd_delete_sam_account (struct pdb_methods *my_methods, struct samu *sampass)
1419 {
1420         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1421
1422         const char *username = pdb_get_username(sampass);
1423
1424         if (del_smbfilepwd_entry(smbpasswd_state, username))
1425                 return NT_STATUS_OK;
1426
1427         return NT_STATUS_UNSUCCESSFUL;
1428 }
1429
1430 static NTSTATUS smbpasswd_rename_sam_account (struct pdb_methods *my_methods, 
1431                                               struct samu *old_acct,
1432                                               const char *newname)
1433 {
1434         char *rename_script = NULL;
1435         struct samu *new_acct = NULL;
1436         bool interim_account = False;
1437         TALLOC_CTX *ctx = talloc_tos();
1438         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
1439
1440         if (!*(lp_renameuser_script()))
1441                 goto done;
1442
1443         if ( !(new_acct = samu_new( NULL )) ) {
1444                 return NT_STATUS_NO_MEMORY;
1445         }
1446         
1447         if ( !pdb_copy_sam_account( new_acct, old_acct ) 
1448                 || !pdb_set_username(new_acct, newname, PDB_CHANGED)) 
1449         {
1450                 goto done;
1451         }
1452
1453         ret = smbpasswd_add_sam_account(my_methods, new_acct);
1454         if (!NT_STATUS_IS_OK(ret))
1455                 goto done;
1456
1457         interim_account = True;
1458
1459         /* rename the posix user */
1460         rename_script = talloc_strdup(ctx,
1461                                 lp_renameuser_script());
1462         if (!rename_script) {
1463                 ret = NT_STATUS_NO_MEMORY;
1464                 goto done;
1465         }
1466
1467         if (*rename_script) {
1468                 int rename_ret;
1469
1470                 rename_script = talloc_string_sub2(ctx,
1471                                         rename_script,
1472                                         "%unew",
1473                                         newname,
1474                                         true,
1475                                         false,
1476                                         true);
1477                 if (!rename_script) {
1478                         ret = NT_STATUS_NO_MEMORY;
1479                         goto done;
1480                 }
1481                 rename_script = talloc_string_sub2(ctx,
1482                                         rename_script,
1483                                         "%uold",
1484                                         pdb_get_username(old_acct),
1485                                         true,
1486                                         false,
1487                                         true);
1488                 if (!rename_script) {
1489                         ret = NT_STATUS_NO_MEMORY;
1490                         goto done;
1491                 }
1492
1493                 rename_ret = smbrun(rename_script, NULL);
1494
1495                 DEBUG(rename_ret ? 0 : 3,("Running the command `%s' gave %d\n", rename_script, rename_ret));
1496
1497                 if (rename_ret == 0) {
1498                         smb_nscd_flush_user_cache();
1499                 }
1500
1501                 if (rename_ret)
1502                         goto done;
1503         } else {
1504                 goto done;
1505         }
1506
1507         smbpasswd_delete_sam_account(my_methods, old_acct);
1508         interim_account = False;
1509
1510 done:
1511         /* cleanup */
1512         if (interim_account)
1513                 smbpasswd_delete_sam_account(my_methods, new_acct);
1514
1515         if (new_acct)
1516                 TALLOC_FREE(new_acct);
1517         
1518         return (ret);   
1519 }
1520
1521 static bool smbpasswd_rid_algorithm(struct pdb_methods *methods)
1522 {
1523         return True;
1524 }
1525
1526 static void free_private_data(void **vp) 
1527 {
1528         struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
1529         
1530         endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
1531         
1532         *privates = NULL;
1533         /* No need to free any further, as it is talloc()ed */
1534 }
1535
1536 struct smbpasswd_search_state {
1537         uint32_t acct_flags;
1538
1539         struct samr_displayentry *entries;
1540         uint32_t num_entries;
1541         ssize_t array_size;
1542         uint32_t current;
1543 };
1544
1545 static void smbpasswd_search_end(struct pdb_search *search)
1546 {
1547         struct smbpasswd_search_state *state = talloc_get_type_abort(
1548                 search->private_data, struct smbpasswd_search_state);
1549         TALLOC_FREE(state);
1550 }
1551
1552 static bool smbpasswd_search_next_entry(struct pdb_search *search,
1553                                         struct samr_displayentry *entry)
1554 {
1555         struct smbpasswd_search_state *state = talloc_get_type_abort(
1556                 search->private_data, struct smbpasswd_search_state);
1557
1558         if (state->current == state->num_entries) {
1559                 return false;
1560         }
1561
1562         entry->idx = state->entries[state->current].idx;
1563         entry->rid = state->entries[state->current].rid;
1564         entry->acct_flags = state->entries[state->current].acct_flags;
1565
1566         entry->account_name = talloc_strdup(
1567                 search->mem_ctx, state->entries[state->current].account_name);
1568         entry->fullname = talloc_strdup(
1569                 search->mem_ctx, state->entries[state->current].fullname);
1570         entry->description = talloc_strdup(
1571                 search->mem_ctx, state->entries[state->current].description);
1572
1573         if ((entry->account_name == NULL) || (entry->fullname == NULL)
1574             || (entry->description == NULL)) {
1575                 DEBUG(0, ("talloc_strdup failed\n"));
1576                 return false;
1577         }
1578
1579         state->current += 1;
1580         return true;
1581 }
1582
1583 static bool smbpasswd_search_users(struct pdb_methods *methods,
1584                                    struct pdb_search *search,
1585                                    uint32_t acct_flags)
1586 {
1587         struct smbpasswd_privates *smbpasswd_state =
1588                 (struct smbpasswd_privates*)methods->private_data;
1589
1590         struct smbpasswd_search_state *search_state;
1591         struct smb_passwd *pwd;
1592         FILE *fp;
1593
1594         search_state = TALLOC_ZERO_P(search->mem_ctx,
1595                                      struct smbpasswd_search_state);
1596         if (search_state == NULL) {
1597                 DEBUG(0, ("talloc failed\n"));
1598                 return false;
1599         }
1600         search_state->acct_flags = acct_flags;
1601
1602         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ,
1603                                &smbpasswd_state->pw_file_lock_depth);
1604
1605         if (fp == NULL) {
1606                 DEBUG(10, ("Unable to open smbpasswd file.\n"));
1607                 TALLOC_FREE(search_state);
1608                 return false;
1609         }
1610
1611         while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
1612                 struct samr_displayentry entry;
1613                 struct samu *user;
1614
1615                 if ((acct_flags != 0)
1616                     && ((acct_flags & pwd->acct_ctrl) == 0)) {
1617                         continue;
1618                 }
1619
1620                 user = samu_new(talloc_tos());
1621                 if (user == NULL) {
1622                         DEBUG(0, ("samu_new failed\n"));
1623                         break;
1624                 }
1625
1626                 if (!build_sam_account(smbpasswd_state, user, pwd)) {
1627                         /* Already got debug msgs... */
1628                         break;
1629                 }
1630
1631                 ZERO_STRUCT(entry);
1632
1633                 entry.acct_flags = pdb_get_acct_ctrl(user);
1634                 sid_peek_rid(pdb_get_user_sid(user), &entry.rid);
1635                 entry.account_name = talloc_strdup(
1636                         search_state, pdb_get_username(user));
1637                 entry.fullname = talloc_strdup(
1638                         search_state, pdb_get_fullname(user));
1639                 entry.description = talloc_strdup(
1640                         search_state, pdb_get_acct_desc(user));
1641
1642                 TALLOC_FREE(user);
1643
1644                 if ((entry.account_name == NULL) || (entry.fullname == NULL)
1645                     || (entry.description == NULL)) {
1646                         DEBUG(0, ("talloc_strdup failed\n"));
1647                         break;
1648                 }
1649
1650                 ADD_TO_LARGE_ARRAY(search_state, struct samr_displayentry,
1651                                    entry, &search_state->entries,
1652                                    &search_state->num_entries,
1653                                    &search_state->array_size);
1654         }
1655
1656         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1657
1658         search->private_data = search_state;
1659         search->next_entry = smbpasswd_search_next_entry;
1660         search->search_end = smbpasswd_search_end;
1661
1662         return true;
1663 }
1664
1665 static NTSTATUS pdb_init_smbpasswd( struct pdb_methods **pdb_method, const char *location )
1666 {
1667         NTSTATUS nt_status;
1668         struct smbpasswd_privates *privates;
1669
1670         if ( !NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method )) ) {
1671                 return nt_status;
1672         }
1673
1674         (*pdb_method)->name = "smbpasswd";
1675
1676         (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
1677         (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
1678         (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
1679         (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
1680         (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
1681         (*pdb_method)->rename_sam_account = smbpasswd_rename_sam_account;
1682         (*pdb_method)->search_users = smbpasswd_search_users;
1683
1684         (*pdb_method)->rid_algorithm = smbpasswd_rid_algorithm;
1685
1686         /* Setup private data and free function */
1687
1688         if ( !(privates = TALLOC_ZERO_P( *pdb_method, struct smbpasswd_privates )) ) {
1689                 DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
1690                 return NT_STATUS_NO_MEMORY;
1691         }
1692
1693         /* Store some config details */
1694
1695         if (location) {
1696                 privates->smbpasswd_file = talloc_strdup(*pdb_method, location);
1697         } else {
1698                 privates->smbpasswd_file = talloc_strdup(*pdb_method, lp_smb_passwd_file());
1699         }
1700         
1701         if (!privates->smbpasswd_file) {
1702                 DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
1703                 return NT_STATUS_NO_MEMORY;
1704         }
1705
1706         (*pdb_method)->private_data = privates;
1707
1708         (*pdb_method)->free_private_data = free_private_data;
1709
1710         return NT_STATUS_OK;
1711 }
1712
1713 NTSTATUS pdb_smbpasswd_init(void) 
1714 {
1715         return smb_register_passdb(PASSDB_INTERFACE_VERSION, "smbpasswd", pdb_init_smbpasswd);
1716 }