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