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