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