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