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